soot-oss / soot

Soot - A Java optimization framework
GNU Lesser General Public License v2.1
2.87k stars 706 forks source link

"java.lang.VerifyError: Register x contains wrong type" after modifying instructions in class file. #1298

Open Instein98 opened 4 years ago

Instein98 commented 4 years ago

This problem happened when we are trying to apply two mutators to a single class file. One is the "return" mutator, trying to directly jump to the return statement. The other is the "goto" mutator, trying to jump to another statement.

Note that we use two int variables and two if statements to avoid the Soot truncating any code during optimization. And the position where those two mutators are applying is designed to trigger this error.

The java source file, the original class file, and the mutant class file are attached (classFiles.zip). By the way, I use java version "1.8.0_161".

The instructions before applying mutator:

i0 = 0
i1 = 0
if i1 >= 100 goto (branch)
i0 = i0 + i1
i1 = i1 + 1
goto [?= (branch)]
if i0 != 4950 goto return
$r0 = <java.lang.System: java.io.PrintStream out>
virtualinvoke $r0.<java.io.PrintStream: void println(java.lang.String)>("nononooooooooooooooooooooo")
return

The instructions after applying mutator:

b0 = 5
b1 = 1
i2 = 0
i3 = b0 + -1
if i3 >= 0 goto i2 = i2 + i5
i4 = b1 + -1
if i4 <= 0 goto return
i5 = 0
if i5 >= 100 goto (branch)
i2 = i2 + i5
i5 = i5 + 1
goto [?= (branch)]
if i2 != 4950 goto return
r0 = <java.lang.System: java.io.PrintStream out>
virtualinvoke r0.<java.io.PrintStream: void println(java.lang.String)>("nononooooooooooooooooooooo")
return

The main code of applying mutators is as follows:

        SootClass c = Scene.v().forceResolve("com.classming.Hello", SootClass.BODIES);
        List<SootMethod> d = c.getMethods();
        for (SootMethod method: d) {
            method.retrieveActiveBody();
        }
        SootMethod test = d.get(2);
        Body body = test.getActiveBody();
        UnitPatchingChain units = body.getUnits();

        // Insert return and goto mutator
        Random random = new Random();
        returnTargetIndex = 0;
        Unit returnTarget = null;
        int gotoTargetIndex = random.nextInt(units.size()-1);
        Unit gotoTarget = null;
        Stmt returnNop = Jimple.v().newNopStmt();
        Stmt gotoNop = Jimple.v().newNopStmt();
        int i = 0;
        for(Unit unit: units){
            if(i==returnTargetIndex)
                returnTarget = unit;
            if(i==gotoTargetIndex)
                gotoTarget = unit;
            i++;
        }
        Local returnNewVar = Jimple.v().newLocal("_M"+"_r", IntType.v());
        Local gotoNewVar = Jimple.v().newLocal("_M"+"_g", IntType.v());
        body.getLocals().add(returnNewVar);
        body.getLocals().add(gotoNewVar);
        AssignStmt assign = Jimple.v().newAssignStmt(returnNewVar, IntConstant.v(1)); // _M = 1
        SubExpr sub = Jimple.v().newSubExpr(returnNewVar, IntConstant.v(1)); // _M-1
        AssignStmt substmt = Jimple.v().newAssignStmt(returnNewVar, sub); // _M = _M-1
        ConditionExpr cond = Jimple.v().newLeExpr(returnNewVar, IntConstant.v(0)); // if _M <= 0
        IfStmt ifGoto = Jimple.v().newIfStmt(cond, returnNop); // if _M <= 0 goto nop

        AssignStmt assign2 = Jimple.v().newAssignStmt(gotoNewVar, IntConstant.v(5)); // _M = 5
        SubExpr sub2 = Jimple.v().newSubExpr(gotoNewVar, IntConstant.v(1)); // _M-1
        AssignStmt substmt2 = Jimple.v().newAssignStmt(gotoNewVar, sub2); // _M = _M-1
        ConditionExpr cond2 = Jimple.v().newGeExpr(gotoNewVar, IntConstant.v(0)); // if _M >= 0
        IfStmt ifGoto2 = Jimple.v().newIfStmt(cond2, gotoNop); // if _M >= 0 goto nop
        for(Unit unit: units){
            if (unit.toString().contains("return") && !unit.toString().contains("goto")){
                units.insertBeforeNoRedirect(returnNop, unit);
                break;
            }
        }

        units.insertBeforeNoRedirect(gotoNop, gotoTarget);
        units.insertBefore(assign, units.getFirst());
        units.insertBefore(assign2, units.getFirst());
        units.insertAfter(ifGoto, returnTarget);
        units.insertAfter(substmt, returnTarget);
        units.insertAfter(ifGoto2, returnTarget);
        units.insertAfter(substmt2, returnTarget);

        // output
        OutputStream streamOut = new JasminOutputStream(new FileOutputStream("com\\classming\\Hello.class"));
        PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));

        JasminClass jasminClass = new soot.jimple.JasminClass(c);
        jasminClass.print(writerOut);
        writerOut.flush();
        streamOut.close();

When we are trying to running the class file after mutation, it goes like:

>java com.classming.Hello
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.VerifyError: (class: com/classming/Hello, method: test signature: ()V) Register 1 contains wrong type
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
        at java.lang.Class.privateGetMethodRecursive(Unknown Source)
        at java.lang.Class.getMethod0(Unknown Source)
        at java.lang.Class.getMethod(Unknown Source)
        at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
ericbodden commented 4 years ago

Could you elaborate please? What is the expected behavior? Is this a bug we can fix in Soot or is it one in your mutator?

Instein98 commented 4 years ago

We think this is a bug of Soot because the instruction insertion is legal, but when we dump the class and run it in JVM, JVM report VerifyError. We expect that after we do the instruction insertion and dump the class, we can run it in JVM normally. It can be a bug of Soot during transforming the Jimple to a class file. In this case, Soot transforms a valid Jimple representation into an invalid class file.

Ginni1110 commented 2 years ago

, has your problem been solved? By the way, what version of soot are you using?