implements PR-O4.6
This commit is contained in:
parent
5abadc62fa
commit
4c52f5de8d
@ -54,7 +54,7 @@ public class BytecodeLinkPrecheckService {
|
|||||||
"truncated opcode at pc=" + pc);
|
"truncated opcode at pc=" + pc);
|
||||||
}
|
}
|
||||||
final var opcode = readU16(module.code(), pc);
|
final var opcode = readU16(module.code(), pc);
|
||||||
final var size = sizeForOpcode(opcode);
|
final var size = sizeForOpcode(opcode, pc);
|
||||||
if (pc + size > module.code().length) {
|
if (pc + size > module.code().length) {
|
||||||
throw new BytecodeMarshalingException(
|
throw new BytecodeMarshalingException(
|
||||||
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
|
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
|
||||||
@ -72,13 +72,16 @@ public class BytecodeLinkPrecheckService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int sizeForOpcode(final int opcode) {
|
private int sizeForOpcode(
|
||||||
return switch (opcode) {
|
final int opcode,
|
||||||
case 0x50, 0x71, 0x72, 0x02, 0x03, 0x04, 0x10, 0x18, 0x40, 0x41, 0x42, 0x43, 0x56 -> 6;
|
final int pc) {
|
||||||
case 0x14, 0x15, 0x52, 0x54 -> 10;
|
try {
|
||||||
case 0x16 -> 3;
|
return BytecodeOpcodeLayout.sizeForOpcode(opcode);
|
||||||
default -> 2;
|
} catch (BytecodeMarshalingException ignored) {
|
||||||
};
|
throw new BytecodeMarshalingException(
|
||||||
|
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_UNKNOWN_OPCODE,
|
||||||
|
"unknown opcode 0x%04X at pc=%d".formatted(opcode, pc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readU16(final byte[] code, final int offset) {
|
private int readU16(final byte[] code, final int offset) {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ public enum BytecodeMarshalingErrorCode {
|
|||||||
MARSHAL_VERIFY_PRECHECK_FUNCTION_BOUNDS_INVALID,
|
MARSHAL_VERIFY_PRECHECK_FUNCTION_BOUNDS_INVALID,
|
||||||
MARSHAL_VERIFY_PRECHECK_EXPORT_FUNC_INDEX_INVALID,
|
MARSHAL_VERIFY_PRECHECK_EXPORT_FUNC_INDEX_INVALID,
|
||||||
MARSHAL_VERIFY_PRECHECK_HOSTCALL_INDEX_INVALID,
|
MARSHAL_VERIFY_PRECHECK_HOSTCALL_INDEX_INVALID,
|
||||||
|
MARSHAL_VERIFY_PRECHECK_UNKNOWN_OPCODE,
|
||||||
MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
|
MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
|
||||||
MARSHAL_VERIFY_PRECHECK_UNTERMINATED_FUNCTION,
|
MARSHAL_VERIFY_PRECHECK_UNTERMINATED_FUNCTION,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
package p.studio.compiler.backend.bytecode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
final class BytecodeOpcodeLayout {
|
||||||
|
private static final Map<Integer, Integer> SIZE_BY_OPCODE = Map.ofEntries(
|
||||||
|
Map.entry(0x01, 2),
|
||||||
|
Map.entry(0x02, 6),
|
||||||
|
Map.entry(0x03, 6),
|
||||||
|
Map.entry(0x04, 6),
|
||||||
|
Map.entry(0x10, 6),
|
||||||
|
Map.entry(0x14, 10),
|
||||||
|
Map.entry(0x15, 10),
|
||||||
|
Map.entry(0x16, 3),
|
||||||
|
Map.entry(0x17, 6),
|
||||||
|
Map.entry(0x18, 6),
|
||||||
|
Map.entry(0x40, 6),
|
||||||
|
Map.entry(0x41, 6),
|
||||||
|
Map.entry(0x42, 6),
|
||||||
|
Map.entry(0x43, 6),
|
||||||
|
Map.entry(0x50, 6),
|
||||||
|
Map.entry(0x51, 2),
|
||||||
|
Map.entry(0x52, 10),
|
||||||
|
Map.entry(0x54, 10),
|
||||||
|
Map.entry(0x56, 6),
|
||||||
|
Map.entry(0x70, 6),
|
||||||
|
Map.entry(0x71, 6),
|
||||||
|
Map.entry(0x72, 6));
|
||||||
|
|
||||||
|
private BytecodeOpcodeLayout() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sizeForOpcode(final int opcode) {
|
||||||
|
final var size = SIZE_BY_OPCODE.get(opcode);
|
||||||
|
if (size == null) {
|
||||||
|
throw new BytecodeMarshalingException(
|
||||||
|
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_UNKNOWN_OPCODE,
|
||||||
|
"unknown opcode 0x%04X".formatted(opcode));
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -35,7 +35,7 @@ public class BytecodePreloadVerifierService {
|
|||||||
BytecodeMarshalingErrorCode.MARSHAL_LINKAGE_RAW_SYSCALL_IN_PRELOAD,
|
BytecodeMarshalingErrorCode.MARSHAL_LINKAGE_RAW_SYSCALL_IN_PRELOAD,
|
||||||
"raw syscall is forbidden in pre-load artifact");
|
"raw syscall is forbidden in pre-load artifact");
|
||||||
}
|
}
|
||||||
final var size = sizeForOpcode(opcode);
|
final var size = sizeForOpcode(opcode, pc);
|
||||||
if (pc + size > end) {
|
if (pc + size > end) {
|
||||||
throw new BytecodeMarshalingException(
|
throw new BytecodeMarshalingException(
|
||||||
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
|
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
|
||||||
@ -52,13 +52,16 @@ public class BytecodePreloadVerifierService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int sizeForOpcode(final int opcode) {
|
private int sizeForOpcode(
|
||||||
return switch (opcode) {
|
final int opcode,
|
||||||
case 0x50, 0x70, 0x71, 0x72, 0x02, 0x03, 0x04, 0x10, 0x18, 0x40, 0x41, 0x42, 0x43, 0x56 -> 6;
|
final int pc) {
|
||||||
case 0x14, 0x15, 0x52, 0x54 -> 10;
|
try {
|
||||||
case 0x16 -> 3;
|
return BytecodeOpcodeLayout.sizeForOpcode(opcode);
|
||||||
default -> 2;
|
} catch (BytecodeMarshalingException ignored) {
|
||||||
};
|
throw new BytecodeMarshalingException(
|
||||||
|
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_UNKNOWN_OPCODE,
|
||||||
|
"unknown opcode 0x%04X at pc=%d".formatted(opcode, pc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readU16(final byte[] code, final int offset) {
|
private int readU16(final byte[] code, final int offset) {
|
||||||
|
|||||||
@ -61,6 +61,21 @@ class BytecodeLinkPrecheckServiceTest {
|
|||||||
assertEquals(BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_HOSTCALL_INDEX_INVALID, thrown.code());
|
assertEquals(BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_HOSTCALL_INDEX_INVALID, thrown.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateMustRejectUnknownOpcode() {
|
||||||
|
final var module = new BytecodeModule(
|
||||||
|
0,
|
||||||
|
ReadOnlyList.empty(),
|
||||||
|
ReadOnlyList.from(new BytecodeModule.FunctionMeta(0, 4, 0, 0, 0, 1)),
|
||||||
|
codeUnknownThenRet(),
|
||||||
|
null,
|
||||||
|
ReadOnlyList.empty(),
|
||||||
|
ReadOnlyList.empty());
|
||||||
|
|
||||||
|
final var thrown = assertThrows(BytecodeMarshalingException.class, () -> service.validate(module));
|
||||||
|
assertEquals(BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_UNKNOWN_OPCODE, thrown.code());
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] codeHostcall(final int index) {
|
private byte[] codeHostcall(final int index) {
|
||||||
final var out = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
|
final var out = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
out.putShort((short) 0x71);
|
out.putShort((short) 0x71);
|
||||||
@ -74,4 +89,11 @@ class BytecodeLinkPrecheckServiceTest {
|
|||||||
out.putShort((short) 0x51);
|
out.putShort((short) 0x51);
|
||||||
return out.array();
|
return out.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] codeUnknownThenRet() {
|
||||||
|
final var out = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
out.putShort((short) 0x7FFF);
|
||||||
|
out.putShort((short) 0x51);
|
||||||
|
return out.array();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,6 +58,21 @@ class BytecodePreloadVerifierServiceTest {
|
|||||||
assertEquals(BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_UNTERMINATED_FUNCTION, thrown.code());
|
assertEquals(BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_UNTERMINATED_FUNCTION, thrown.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifyMustRejectUnknownOpcode() {
|
||||||
|
final var module = new BytecodeModule(
|
||||||
|
0,
|
||||||
|
ReadOnlyList.empty(),
|
||||||
|
ReadOnlyList.from(new BytecodeModule.FunctionMeta(0, 4, 0, 0, 0, 1)),
|
||||||
|
codeUnknownThenRet(),
|
||||||
|
null,
|
||||||
|
ReadOnlyList.empty(),
|
||||||
|
ReadOnlyList.empty());
|
||||||
|
|
||||||
|
final var thrown = assertThrows(BytecodeMarshalingException.class, () -> service.verify(module));
|
||||||
|
assertEquals(BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_UNKNOWN_OPCODE, thrown.code());
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] codeHostcall(final int index) {
|
private byte[] codeHostcall(final int index) {
|
||||||
final var out = ByteBuffer.allocate(6).order(ByteOrder.LITTLE_ENDIAN);
|
final var out = ByteBuffer.allocate(6).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
out.putShort((short) 0x71);
|
out.putShort((short) 0x71);
|
||||||
@ -77,4 +92,11 @@ class BytecodePreloadVerifierServiceTest {
|
|||||||
out.putShort((short) 0x51);
|
out.putShort((short) 0x51);
|
||||||
return out.array();
|
return out.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] codeUnknownThenRet() {
|
||||||
|
final var out = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
out.putShort((short) 0x7FFF);
|
||||||
|
out.putShort((short) 0x51);
|
||||||
|
return out.array();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user