Skip to content

Fix suggestions for Soot1577Test #855

@Momo-Not-Emo

Description

@Momo-Not-Emo

Soot1577Test was introduced in PR #405 but is currently ignored due to a message stating "conversion fails - could be a dex2jar conversion problem." I have successfully ported this test case to Soot, ensuring its success (refer to soot-oss/soot#2047).

In attempting to address the issue in SootUp by aligning its behavior with Soot, I encountered more intricate reasons for its failure. In this report, I will outline my analysis of the problem and provide suggestions for fixing it. I hope this information proves useful in resolving the issues.

Problem 1: Unmatched ClassType warning

Currently, Soot1577Test gives a warning regarding unmatched ClassType:

[main] WARN sootup.java.bytecode.frontend.AsmJavaClassProvider - The given ClassType 'g' did not match the found ClassType in the compilation unit 'cn/com/chinatelecom/account/api/c/g'. Possibly, the AnalysisInputLocation points to a subfolder already including the PackageName directory while the ClassType you wanted to retrieve is missing a PackageName.

While this warning does not halt execution, it's suggested to create missing directories for the resource file g.class based on its package information (i.e., soot-1577/g.class —> soot-1577/cn/com/chinatelecom/account/api/c/g.class).

Problem 2: Illegal Assignment statement

The stack trace indicates an Illegal Assignment statement issue:

Caused by: java.lang.RuntimeException: Illegal Assignment statement. Make sure that right hand side (@caughtexception) is a valid operand.
at sootup.core.jimple.common.stmt.JAssignStmt.<init>(JAssignStmt.java:77)
at sootup.core.jimple.Jimple.newAssignStmt(Jimple.java:497)
at sootup.java.bytecode.frontend.Operand.changeStackLocal(Operand.java:112)
at sootup.java.bytecode.frontend.OperandMerging.mergeInputs(OperandMerging.java:162)
at sootup.java.bytecode.frontend.AsmMethodSource.convertMethodInsn(AsmMethodSource.java:1028)
at sootup.java.bytecode.frontend.AsmMethodSource.convert(AsmMethodSource.java:1441)
at sootup.java.bytecode.frontend.AsmMethodSource.resolveBody(AsmMethodSource.java:192)

The problematic code is

void changeStackLocal(Local newStackLocal) {
   ...

    Stmt stmt = methodSource.getStmt(insn);
    if (!(stmt instanceof JAssignStmt)) {
      // emit `$newStackLocal = value`
      methodSource.setStmt(insn, Jimple.newAssignStmt(newStackLocal, value, positionInfo));
    } else {
      JAssignStmt assignStmt = (JAssignStmt) stmt;
      assert assignStmt.getLeftOp() == oldStackLocal || assignStmt.getLeftOp() == newStackLocal;
      // replace `$oldStackLocal = value` with `$newStackLocal = value`
      methodSource.replaceStmt(assignStmt, assignStmt.withVariable(newStackLocal));
    }

    // Replace all usages of `oldStackLocal` with `newStackLocal`
   ...
}

When the stmt is an instance of JNopStmt, the value is @caughtexception, which is a valid right-hand-side operand in a JIdentityStmt but not in a JAssignStmt.

I suggest not altering the state of the methodSource field when a JNopStmt appears, aligning with the corresponding behavior in Soot. In Soot, the relevant field for Operand#methodSource is AsmMethodSource#units, as both store mappings from Node objects to Stmt objects. In debug mode, I observed that Soot retains mappings like {LabelNode@xxxx} -> {JNopStmt@xxxx} “nop”.

Here is my proposed fix:

void changeStackLocal(Local newStackLocal) {
   ...
    Stmt stmt = methodSource.getStmt(insn);

    if (stmt instanceof JNopStmt)
    return;

    if (!(stmt instanceof JAssignStmt)) {  ... } else { ... }
    ...
}

Problem 3: Null handler while invoking buildTraps()

There is an issue with a null handler while invoking buildTraps() :

java.lang.RuntimeException: Failed to convert <cn.com.chinatelecom.account.api.c.g: int h(android.content.Context)>
	at sootup.java.bytecode.frontend.AsmMethodSource.resolveBody(AsmMethodSource.java:210)
	at sootup.core.model.SootMethod.lazyBodyInitializer(SootMethod.java:98)
	at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:181)
	at sootup.core.model.SootMethod.getBody(SootMethod.java:177)
	at java.lang.Iterable.forEach(Iterable.java:75)
	at sootup.java.bytecode.Soot1577Test.test(Soot1577Test.java:26)
Caused by: java.lang.IllegalStateException: Label for the TrapHandler org.objectweb.asm.tree.LabelNode@b3d7190 has no associated Stmt to jump to.
	at sootup.java.bytecode.frontend.AsmMethodSource.buildTraps(AsmMethodSource.java:1597)
	at sootup.java.bytecode.frontend.AsmMethodSource.arrangeStmts(AsmMethodSource.java:1690)
	at sootup.java.bytecode.frontend.AsmMethodSource.resolveBody(AsmMethodSource.java:208)

Root Cause

The issue stems from buildTraps() being invoked before adding handlerStmt objects from inlineExceptionHandlers into trapHandler during the execution of the arrangeStmts(...) method. This results in null values in trapHandler for keys that are present, as illustrated in the image below.

image

Comparing Implementations in soot and SootUp

While monitoring the filling process of trapHandler in Soot, I identified the method Operand: void emitUnits(), which corresponds to the method AsmMethodSource: void arrangeStmts(…) in SootUp.

private void emitUnits() {
    // Step 1: Identify all exception handlers (Unit objects instanceof IdentityStmt) in units, then put them into this.trapHandlers and append to this.body.unitChain
     ...
    //  Step 2: Iterate through this.inlineExceptionHandlers, and put the handlers into this.trapHandlers, appending them to this.body.unitChain
    for (LabelNode ln : this.inlineExceptionHandlers.keySet()) {
      Unit handler = this.inlineExceptionHandlers.get(ln);
      emitUnits(handler, body.getUnits());

      Collection<UnitBox> traps = trapHandlers.get(ln);
      for (UnitBox ub : traps) {
        ub.setUnit(handler);
      }

      // We need to jump to the original implementation
      Unit targetUnit = units.get(ln);  // JNopStmt sharing the same LabelNode with the handler
      GotoStmt gotoImpl = Jimple.v().newGotoStmt(targetUnit);
      body.getUnits().add(gotoImpl);
    }

    ...
}

The flawed implementation in SootUp is as follows:

private void arrangeStmts(...) {
    // Step 1: Identify all exception handlers (Unit objects instanceof IdentityStmt) in units, then put them into this.trapHandlers and append to stmtList
     ...

    final List<Trap> traps = buildTraps();
    graph.initializeWith(stmtList, branchingMap, traps);

    // Step: Iterate through this.inlineExceptionHandlers, and put the handlers into this.trapHandlers, appending them to stmtList
    // Emit the inline exception handler blocks i.e. those that are reachable without exceptional flow
    // FIXME:[ms] the following code seems odd.. we need a testcase to test inlineexceptionhandling!
    for (Entry<LabelNode, JIdentityStmt> entry : inlineExceptionHandlers.entrySet()) {

      JIdentityStmt handlerStmt = entry.getValue();
      emitStmt(handlerStmt, stmtList);
      trapHandler.put(entry.getKey(), handlerStmt);
      // TODO: update handlerStmts positioninfo!

      // jump back to the original implementation
      JGotoStmt gotoStmt = Jimple.newGotoStmt(handlerStmt.getPositionInfo());
      stmtList.add(gotoStmt);

      // add stmtList to graph
      graph.addBlock(stmtList, currentTraps);
      stmtList.clear();

      // connect tail of stmtList with its target
      Stmt targetStmt = insnToStmt.get(entry.getKey());
      graph.putEdge(gotoStmt, 0, targetStmt);
    }
  }

Fix suggestion

Move the final List<Trap> traps = buildTraps(); graph.initializeWith(stmtList, branchingMap, traps); after emitting the inline exception handler blocks.

Problem: Unable to link the handlerStmt to the targetStmt via a GotoStmt due to the absence of StmtPositionInfo for all Stmt objects being "No StmtPositionInfo."

image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions