implements PR-O4.6

This commit is contained in:
bQUARKz 2026-03-07 19:46:11 +00:00
parent 5abadc62fa
commit 4c52f5de8d
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
6 changed files with 109 additions and 16 deletions

View File

@ -54,7 +54,7 @@ public class BytecodeLinkPrecheckService {
"truncated opcode at pc=" + 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) {
throw new BytecodeMarshalingException(
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
@ -72,13 +72,16 @@ public class BytecodeLinkPrecheckService {
}
}
private int sizeForOpcode(final int opcode) {
return switch (opcode) {
case 0x50, 0x71, 0x72, 0x02, 0x03, 0x04, 0x10, 0x18, 0x40, 0x41, 0x42, 0x43, 0x56 -> 6;
case 0x14, 0x15, 0x52, 0x54 -> 10;
case 0x16 -> 3;
default -> 2;
};
private int sizeForOpcode(
final int opcode,
final int pc) {
try {
return BytecodeOpcodeLayout.sizeForOpcode(opcode);
} 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) {

View File

@ -10,6 +10,7 @@ public enum BytecodeMarshalingErrorCode {
MARSHAL_VERIFY_PRECHECK_FUNCTION_BOUNDS_INVALID,
MARSHAL_VERIFY_PRECHECK_EXPORT_FUNC_INDEX_INVALID,
MARSHAL_VERIFY_PRECHECK_HOSTCALL_INDEX_INVALID,
MARSHAL_VERIFY_PRECHECK_UNKNOWN_OPCODE,
MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
MARSHAL_VERIFY_PRECHECK_UNTERMINATED_FUNCTION,
}

View File

@ -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;
}
}

View File

@ -35,7 +35,7 @@ public class BytecodePreloadVerifierService {
BytecodeMarshalingErrorCode.MARSHAL_LINKAGE_RAW_SYSCALL_IN_PRELOAD,
"raw syscall is forbidden in pre-load artifact");
}
final var size = sizeForOpcode(opcode);
final var size = sizeForOpcode(opcode, pc);
if (pc + size > end) {
throw new BytecodeMarshalingException(
BytecodeMarshalingErrorCode.MARSHAL_VERIFY_PRECHECK_TRUNCATED_INSTRUCTION,
@ -52,13 +52,16 @@ public class BytecodePreloadVerifierService {
}
}
private int sizeForOpcode(final int opcode) {
return switch (opcode) {
case 0x50, 0x70, 0x71, 0x72, 0x02, 0x03, 0x04, 0x10, 0x18, 0x40, 0x41, 0x42, 0x43, 0x56 -> 6;
case 0x14, 0x15, 0x52, 0x54 -> 10;
case 0x16 -> 3;
default -> 2;
};
private int sizeForOpcode(
final int opcode,
final int pc) {
try {
return BytecodeOpcodeLayout.sizeForOpcode(opcode);
} 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) {

View File

@ -61,6 +61,21 @@ class BytecodeLinkPrecheckServiceTest {
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) {
final var out = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
out.putShort((short) 0x71);
@ -74,4 +89,11 @@ class BytecodeLinkPrecheckServiceTest {
out.putShort((short) 0x51);
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();
}
}

View File

@ -58,6 +58,21 @@ class BytecodePreloadVerifierServiceTest {
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) {
final var out = ByteBuffer.allocate(6).order(ByteOrder.LITTLE_ENDIAN);
out.putShort((short) 0x71);
@ -77,4 +92,11 @@ class BytecodePreloadVerifierServiceTest {
out.putShort((short) 0x51);
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();
}
}