Commit 94cef68c authored by Eric Bruneton's avatar Eric Bruneton
Browse files

Check that frames are valid for V1_7 or more classes, if COMPUTE_FRAMES is not used.

parent de38696f
Pipeline #20626 passed with stage
in 5 minutes and 33 seconds
......@@ -464,7 +464,8 @@ public class CheckClassAdapter extends ClassVisitor {
if (checkDataFlow) {
if (cv instanceof ClassWriter) {
methodVisitor =
new CheckMethodAdapter.MethodWriterWrapper(api, (ClassWriter) cv, methodVisitor);
new CheckMethodAdapter.MethodWriterWrapper(
api, version, (ClassWriter) cv, methodVisitor);
}
checkMethodAdapter =
new CheckMethodAdapter(api, access, name, descriptor, methodVisitor, labelInsnIndices);
......
......@@ -102,17 +102,16 @@ class CheckFrameAnalyzer<V extends Value> extends Analyzer<V> {
currentFrame.init(oldFrame).execute(insnNode, interpreter);
if (insnNode instanceof JumpInsnNode) {
JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;
if (insnOpcode != GOTO && insnOpcode != JSR) {
checkFrame(insnIndex + 1, currentFrame, /* requireFrame = */ false);
} else if (insnOpcode == GOTO) {
endControlFlow(insnIndex);
}
int jumpInsnIndex = insnList.indexOf(jumpInsn.label);
if (insnOpcode == JSR) {
throw new AnalyzerException(insnNode, "JSR instructions are unsupported");
}
JumpInsnNode jumpInsn = (JumpInsnNode) insnNode;
int targetInsnIndex = insnList.indexOf(jumpInsn.label);
checkFrame(targetInsnIndex, currentFrame, /* requireFrame = */ true);
if (insnOpcode == GOTO) {
endControlFlow(insnIndex);
} else {
checkFrame(jumpInsnIndex, currentFrame, /* requireFrame = */ true);
checkFrame(insnIndex + 1, currentFrame, /* requireFrame = */ false);
}
} else if (insnNode instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnNode;
......
......@@ -447,16 +447,24 @@ public class CheckMethodAdapter extends MethodVisitor {
new MethodNode(api, access, name, descriptor, null, null) {
@Override
public void visitEnd() {
Analyzer<BasicValue> analyzer = new Analyzer<>(new BasicVerifier());
try {
boolean checkMaxStackAndLocals = false;
boolean checkFrames = false;
if (methodVisitor instanceof MethodWriterWrapper) {
MethodWriterWrapper methodWriter = (MethodWriterWrapper) methodVisitor;
// If 'methodVisitor' is a MethodWriter of a ClassWriter with no flags to compute the
// max stack and locals nor the stack map frames, we know that valid max stack and
// locals must be provided. Otherwise we assume they are not needed at this stage.
// TODO(ebruneton): similarly, check that valid stack map frames are provided if the
// class writer has no flags to compute them, and the class version is V1_7 or more.
boolean checkMaxStackAndLocals =
(methodVisitor instanceof MethodWriterWrapper)
&& !((MethodWriterWrapper) methodVisitor).computesMaxs();
checkMaxStackAndLocals = !methodWriter.computesMaxs();
// If 'methodVisitor' is a MethodWriter of a ClassWriter with no flags to compute the
// stack map frames, we know that valid frames must be provided. Otherwise we assume
// they are not needed at this stage.
checkFrames = methodWriter.requiresFrames() && !methodWriter.computesFrames();
}
Analyzer<BasicValue> analyzer =
checkFrames
? new CheckFrameAnalyzer<>(new BasicVerifier())
: new Analyzer<>(new BasicVerifier());
try {
if (checkMaxStackAndLocals) {
analyzer.analyze("dummy", this);
} else {
......@@ -1448,15 +1456,31 @@ public class CheckMethodAdapter extends MethodVisitor {
static class MethodWriterWrapper extends MethodVisitor {
/** The class version number. */
private final int version;
private final ClassWriter owner;
MethodWriterWrapper(final int api, final ClassWriter owner, final MethodVisitor methodWriter) {
MethodWriterWrapper(
final int api,
final int version,
final ClassWriter owner,
final MethodVisitor methodWriter) {
super(api, methodWriter);
this.version = version;
this.owner = owner;
}
boolean computesMaxs() {
return owner.hasFlags(ClassWriter.COMPUTE_MAXS) || owner.hasFlags(ClassWriter.COMPUTE_FRAMES);
}
boolean computesFrames() {
return owner.hasFlags(ClassWriter.COMPUTE_FRAMES);
}
boolean requiresFrames() {
return (version & 0xFFFF) >= Opcodes.V1_7;
}
}
}
......@@ -86,19 +86,26 @@ class CheckFrameAnalyzerTest extends AsmTest implements Opcodes {
@Test
void testAnalyze_missingFrameAtJumpTarget() {
MethodNode methodNode =
new MethodNodeBuilder().iconst_0().ifne(label0).iconst_0().label(label0).vreturn().build();
new MethodNodeBuilder().iconst_0().ifne(label0).label(label0).vreturn().build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
String message = assertThrows(AnalyzerException.class, analyze).getMessage();
assertTrue(
message.contains("Error at instruction 1: Expected stack map frame at instruction 3"));
message.contains("Error at instruction 1: Expected stack map frame at instruction 2"));
}
@Test
void testAnalyze_missingFrameAfterGoto() {
MethodNode methodNode =
new MethodNodeBuilder().nop().go(label0).nop().label(label0).vreturn().build();
new MethodNodeBuilder()
.nop()
.go(label0)
.nop()
.label(label0)
.frame(Opcodes.F_SAME, null, null)
.vreturn()
.build();
Executable analyze = () -> newAnalyzer().analyze(CLASS_NAME, methodNode);
......
......@@ -1108,6 +1108,7 @@ class CheckMethodAdapterTest extends AsmTest implements Opcodes {
MethodVisitor methodVisitor =
new CheckMethodAdapter.MethodWriterWrapper(
/* latest api = */ Opcodes.ASM9,
/* version = */ Opcodes.V1_5,
classWriter,
new MethodVisitor(/* latest api = */ Opcodes.ASM9) {});
MethodVisitor dataFlowCheckMethodAdapter =
......@@ -1123,11 +1124,12 @@ class CheckMethodAdapterTest extends AsmTest implements Opcodes {
}
@Test
void testVisitEnd_noInvalidMaxStackIfClassWriterWithComputeFrales() {
void testVisitEnd_noInvalidMaxStackIfClassWriterWithComputeFrames() {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodVisitor methodVisitor =
new CheckMethodAdapter.MethodWriterWrapper(
/* latest api = */ Opcodes.ASM9,
/* version = */ Opcodes.V1_5,
classWriter,
new MethodVisitor(/* latest api = */ Opcodes.ASM9) {});
MethodVisitor dataFlowCheckMethodAdapter =
......@@ -1148,6 +1150,7 @@ class CheckMethodAdapterTest extends AsmTest implements Opcodes {
MethodVisitor methodVisitor =
new CheckMethodAdapter.MethodWriterWrapper(
/* latest api = */ Opcodes.ASM9,
/* version = */ Opcodes.V1_5,
classWriter,
new MethodVisitor(/* latest api = */ Opcodes.ASM9) {});
MethodVisitor dataFlowCheckMethodAdapter =
......@@ -1166,6 +1169,60 @@ class CheckMethodAdapterTest extends AsmTest implements Opcodes {
.startsWith("Error at instruction 0: Insufficient maximum stack size. m(I)I"));
}
@Test
void testVisitEnd_noMissingFrameIfClassWriterWithComputeFrames() {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodVisitor methodVisitor =
new CheckMethodAdapter.MethodWriterWrapper(
/* latest api = */ Opcodes.ASM9,
/* version = */ Opcodes.V1_7,
classWriter,
new MethodVisitor(/* latest api = */ Opcodes.ASM9) {});
MethodVisitor dataFlowCheckMethodAdapter =
new CheckMethodAdapter(ACC_PUBLIC, "m", "(I)I", methodVisitor, new HashMap<>());
Label label = new Label();
dataFlowCheckMethodAdapter.visitCode();
dataFlowCheckMethodAdapter.visitVarInsn(ILOAD, 1);
dataFlowCheckMethodAdapter.visitJumpInsn(IFEQ, label);
dataFlowCheckMethodAdapter.visitLabel(label);
dataFlowCheckMethodAdapter.visitInsn(ICONST_0);
dataFlowCheckMethodAdapter.visitInsn(IRETURN);
dataFlowCheckMethodAdapter.visitMaxs(2, 2);
Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd();
assertDoesNotThrow(visitEnd);
}
@Test
void testVisitEnd_missingFrameIfClassWriterWithoutComputeFrames() {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
MethodVisitor methodVisitor =
new CheckMethodAdapter.MethodWriterWrapper(
/* latest api = */ Opcodes.ASM9,
/* version = */ Opcodes.V1_7,
classWriter,
new MethodVisitor(/* latest api = */ Opcodes.ASM9) {});
MethodVisitor dataFlowCheckMethodAdapter =
new CheckMethodAdapter(ACC_PUBLIC, "m", "(I)I", methodVisitor, new HashMap<>());
Label label = new Label();
dataFlowCheckMethodAdapter.visitCode();
dataFlowCheckMethodAdapter.visitVarInsn(ILOAD, 1);
dataFlowCheckMethodAdapter.visitJumpInsn(IFEQ, label);
dataFlowCheckMethodAdapter.visitLabel(label);
dataFlowCheckMethodAdapter.visitInsn(ICONST_0);
dataFlowCheckMethodAdapter.visitInsn(IRETURN);
dataFlowCheckMethodAdapter.visitMaxs(2, 2);
Executable visitEnd = () -> dataFlowCheckMethodAdapter.visitEnd();
Exception exception = assertThrows(IllegalArgumentException.class, visitEnd);
assertTrue(
exception
.getMessage()
.startsWith("Error at instruction 1: Expected stack map frame at instruction 2 m(I)I"));
}
@Test
void testVisitEnd_invalidReturnType() {
MethodVisitor dataFlowCheckMethodAdapter =
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment