mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8349624: Validation for slot missing in CodeBuilder local variable instructions
Reviewed-by: asotona
This commit is contained in:
parent
a1bcda2476
commit
32dc41c9f7
2 changed files with 60 additions and 23 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
|
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
|
@ -484,7 +484,7 @@ public final class DirectCodeBuilder
|
||||||
bytecodesBufWriter.writeU1(opcode.bytecode());
|
bytecodesBufWriter.writeU1(opcode.bytecode());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instruction version, refer to opcode
|
// Instruction version, refer to opcode, trusted
|
||||||
public void writeLocalVar(Opcode opcode, int slot) {
|
public void writeLocalVar(Opcode opcode, int slot) {
|
||||||
if (opcode.isWide()) {
|
if (opcode.isWide()) {
|
||||||
bytecodesBufWriter.writeU2U2(opcode.bytecode(), slot);
|
bytecodesBufWriter.writeU2U2(opcode.bytecode(), slot);
|
||||||
|
@ -493,12 +493,12 @@ public final class DirectCodeBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shortcut version, refer to and validate slot
|
// local var access, not a trusted write method, needs slot validation
|
||||||
private void writeLocalVar(int bytecode, int slot) {
|
private void localAccess(int bytecode, int slot) {
|
||||||
// TODO validation like (slot & 0xFFFF) == slot
|
if ((slot & ~0xFF) == 0) {
|
||||||
if (slot < 256) {
|
|
||||||
bytecodesBufWriter.writeU1U1(bytecode, slot);
|
bytecodesBufWriter.writeU1U1(bytecode, slot);
|
||||||
} else {
|
} else {
|
||||||
|
BytecodeHelpers.validateSlot(slot);
|
||||||
bytecodesBufWriter.writeU1U1U2(WIDE, bytecode, slot);
|
bytecodesBufWriter.writeU1U1U2(WIDE, bytecode, slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -989,7 +989,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(ALOAD_0 + slot);
|
bytecodesBufWriter.writeU1(ALOAD_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(ALOAD, slot);
|
localAccess(ALOAD, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1017,7 +1017,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(ASTORE_0 + slot);
|
bytecodesBufWriter.writeU1(ASTORE_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(ASTORE, slot);
|
localAccess(ASTORE, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1100,7 +1100,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(DLOAD_0 + slot);
|
bytecodesBufWriter.writeU1(DLOAD_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(DLOAD, slot);
|
localAccess(DLOAD, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1134,7 +1134,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(DSTORE_0 + slot);
|
bytecodesBufWriter.writeU1(DSTORE_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(DSTORE, slot);
|
localAccess(DSTORE, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1246,7 +1246,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(FLOAD_0 + slot);
|
bytecodesBufWriter.writeU1(FLOAD_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(FLOAD, slot);
|
localAccess(FLOAD, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1280,7 +1280,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(FSTORE_0 + slot);
|
bytecodesBufWriter.writeU1(FSTORE_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(FSTORE, slot);
|
localAccess(FSTORE, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1506,7 +1506,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(ILOAD_0 + slot);
|
bytecodesBufWriter.writeU1(ILOAD_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(ILOAD, slot);
|
localAccess(ILOAD, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1606,7 +1606,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(ISTORE_0 + slot);
|
bytecodesBufWriter.writeU1(ISTORE_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(ISTORE, slot);
|
localAccess(ISTORE, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1701,7 +1701,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(LLOAD_0 + slot);
|
bytecodesBufWriter.writeU1(LLOAD_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(LLOAD, slot);
|
localAccess(LLOAD, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -1753,7 +1753,7 @@ public final class DirectCodeBuilder
|
||||||
if (slot >= 0 && slot <= 3) {
|
if (slot >= 0 && slot <= 3) {
|
||||||
bytecodesBufWriter.writeU1(LSTORE_0 + slot);
|
bytecodesBufWriter.writeU1(LSTORE_0 + slot);
|
||||||
} else {
|
} else {
|
||||||
writeLocalVar(LSTORE, slot);
|
localAccess(LSTORE, slot);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -32,13 +32,17 @@ import java.lang.classfile.*;
|
||||||
import java.lang.classfile.constantpool.ClassEntry;
|
import java.lang.classfile.constantpool.ClassEntry;
|
||||||
import java.lang.classfile.constantpool.ConstantPoolBuilder;
|
import java.lang.classfile.constantpool.ConstantPoolBuilder;
|
||||||
import java.lang.classfile.instruction.*;
|
import java.lang.classfile.instruction.*;
|
||||||
|
import java.lang.constant.ClassDesc;
|
||||||
|
import java.lang.reflect.Parameter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.ObjIntConsumer;
|
import java.util.function.ObjIntConsumer;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
|
|
||||||
|
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||||
import static java.lang.constant.ConstantDescs.*;
|
import static java.lang.constant.ConstantDescs.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static java.lang.classfile.Opcode.*;
|
import static java.lang.classfile.Opcode.*;
|
||||||
|
@ -110,17 +114,23 @@ class InstructionValidationTest {
|
||||||
var fails = r.shouldFail;
|
var fails = r.shouldFail;
|
||||||
var i = r.slot;
|
var i = r.slot;
|
||||||
for (var fac : cbFactories) {
|
for (var fac : cbFactories) {
|
||||||
//check(fails, () -> execute(cob -> fac.accept(cob, i)));
|
if (fails) {
|
||||||
|
ensureFailFast(i, cob -> fac.accept(cob, i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (TypeKind tk : TypeKind.values()) {
|
for (TypeKind tk : TypeKind.values()) {
|
||||||
if (tk == TypeKind.VOID)
|
if (tk == TypeKind.VOID)
|
||||||
continue;
|
continue;
|
||||||
//check(fails, () -> execute(cob -> cob.loadLocal(tk, i)));
|
if (fails) {
|
||||||
//check(fails, () -> execute(cob -> cob.storeLocal(tk, i)));
|
ensureFailFast(i, cob -> cob.loadLocal(tk, i));
|
||||||
|
ensureFailFast(i, cob -> cob.storeLocal(tk, i));
|
||||||
|
}
|
||||||
check(fails, () -> LoadInstruction.of(tk, i));
|
check(fails, () -> LoadInstruction.of(tk, i));
|
||||||
check(fails, () -> StoreInstruction.of(tk, i));
|
check(fails, () -> StoreInstruction.of(tk, i));
|
||||||
}
|
}
|
||||||
//check(fails, () -> execute(cob -> cob.iinc(i, 1)));
|
if (fails) {
|
||||||
|
ensureFailFast(i, cob -> cob.iinc(i, 1));
|
||||||
|
}
|
||||||
check(fails, () -> IncrementInstruction.of(i, 1));
|
check(fails, () -> IncrementInstruction.of(i, 1));
|
||||||
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i));
|
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i));
|
||||||
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i));
|
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i));
|
||||||
|
@ -150,6 +160,31 @@ class InstructionValidationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CodeBuilder can fail with IAE due to other reasons, so we cannot check
|
||||||
|
// "success" but can ensure things fail fast
|
||||||
|
static void ensureFailFast(int value, Consumer<CodeBuilder> action) {
|
||||||
|
// Can fail with AssertionError instead of IAE
|
||||||
|
Consumer<CodeBuilder> checkedAction = cob -> {
|
||||||
|
action.accept(cob);
|
||||||
|
fail("Bad slot access did not fail fast: " + value);
|
||||||
|
};
|
||||||
|
var cf = ClassFile.of();
|
||||||
|
CodeTransform noopCodeTransform = CodeBuilder::with;
|
||||||
|
// Direct builders
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> cf.build(ClassDesc.of("Test"), clb -> clb
|
||||||
|
.withMethodBody("test", MTD_void, ACC_STATIC, checkedAction)));
|
||||||
|
// Chained builders
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> cf.build(ClassDesc.of("Test"), clb -> clb
|
||||||
|
.withMethodBody("test", MTD_void, ACC_STATIC, cob -> cob
|
||||||
|
.transforming(CodeTransform.ACCEPT_ALL, checkedAction))));
|
||||||
|
var classTemplate = cf.build(ClassDesc.of("Test"), clb -> clb
|
||||||
|
.withMethodBody("test", MTD_void, ACC_STATIC, CodeBuilder::return_));
|
||||||
|
// Indirect builders
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> cf.transformClass(cf.parse(classTemplate),
|
||||||
|
ClassTransform.transformingMethodBodies(CodeTransform.endHandler(checkedAction)
|
||||||
|
.andThen(noopCodeTransform))));
|
||||||
|
}
|
||||||
|
|
||||||
static void check(boolean fails, Executable exec) {
|
static void check(boolean fails, Executable exec) {
|
||||||
if (fails) {
|
if (fails) {
|
||||||
assertThrows(IllegalArgumentException.class, exec);
|
assertThrows(IllegalArgumentException.class, exec);
|
||||||
|
@ -163,8 +198,10 @@ class InstructionValidationTest {
|
||||||
IncrementInstruction.of(0, 2);
|
IncrementInstruction.of(0, 2);
|
||||||
IncrementInstruction.of(0, Short.MAX_VALUE);
|
IncrementInstruction.of(0, Short.MAX_VALUE);
|
||||||
IncrementInstruction.of(0, Short.MIN_VALUE);
|
IncrementInstruction.of(0, Short.MIN_VALUE);
|
||||||
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, Short.MAX_VALUE + 1));
|
for (int i : new int[] {Short.MIN_VALUE - 1, Short.MAX_VALUE + 1}) {
|
||||||
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, Short.MIN_VALUE - 1));
|
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, i));
|
||||||
|
ensureFailFast(i, cob -> cob.iinc(0, i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue