soot icon indicating copy to clipboard operation
soot copied to clipboard

ConcurrentModificationException when using Options.v().set_ignore_resolution_errors(true);

Open muthu30011997 opened this issue 5 years ago • 8 comments

I got below exception

java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at soot.jimple.toolkits.annotation.LineNumberAdder.internalTransform(LineNumberAdder.java:58) at soot.PackManager.runPacksNormally(PackManager.java:491) at soot.PackManager.runPacks(PackManager.java:419) at soot.Main.run(Main.java:269) at soot.Main.main(Main.java:141)

when i set Options.v().set_ignore_resolution_errors(true);

How can i solve this problem ?

muthu30011997 avatar Aug 30 '19 06:08 muthu30011997

Any updates on this ?

muthu30011997 avatar Nov 29 '19 07:11 muthu30011997

I have a very small reproducer for this

cat C.java I.java && javac C.java I.java && jar cf C.jar C.class && java -cp ... soot.Main -process-dir C.jar -keep-line-number -ignore-resolution-errors -allow-phantom-refs
abstract class C implements I {
    void bar() {
        try {
            foo();
        } catch (Exception e) {
            throw e;
        }
    }
}
interface I {
    void foo();
}
Soot started on Fri Jul 01 19:34:23 UTC 2022
java.util.ConcurrentModificationException
        at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
        at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
        at soot.jimple.toolkits.annotation.LineNumberAdder.internalTransform(LineNumberAdder.java:57)
        at soot.PackManager.runPacksNormally(PackManager.java:496)
        at soot.PackManager.runPacks(PackManager.java:425)
        at soot.Main.run(Main.java:280)
        at soot.Main.main(Main.java:142)

michael-emmi avatar Jul 01 '22 19:07 michael-emmi

And here is my rough understanding of the root cause:

  1. line number adder iterates over the methods of class C
  2. runs jb pack on body of bar, including the unreachable code eliminator (uce)
  3. uce creates an exceptional unit graph for bar, initializing exception destinations using unit throw analysis
  4. unit throw analysis processes method references, trying to resolve foo
  5. foo cannot be resolved, as it is defined in interface I which is not included in input jars
  6. fallback logic in soot method ref impl creates an artificial method for foo in class C
  7. now the list of methods for class C has changed, causing the concurrent modification exception in line number adder

michael-emmi avatar Jul 01 '22 19:07 michael-emmi

It seems like there are at least two approaches to avoid CME here:

  • iterate over snapshot(s) of method list, or
  • do not change method list.

For the first, one should decide whether to include newly-added methods, e.g., by continuing until snapshots stabilize.

For the second, I am wondering: should the declaration of method foo be added to class C, or is that the bug?

michael-emmi avatar Jul 06 '22 15:07 michael-emmi

@linghuiluo @StevenArzt @mbenz89 any thoughts about the above?

michael-emmi avatar Jul 06 '22 15:07 michael-emmi

I would go for the first approach, i.e., create a copy of the method list. Only phantom methods should be created during the (transitive) method resolving started by the LineNumberAdder, and there's nothing to add in those.

StevenArzt avatar Jul 06 '22 17:07 StevenArzt

Only phantom methods should be created during the (transitive) method resolving started by the LineNumberAdder

But should phantom methods be added to the class for which bodies are already loaded?

In this example, class C does not declare method foo, yet foo gets added as a phantom method to class C. Should this be happening?

michael-emmi avatar Jul 06 '22 18:07 michael-emmi

Yes, the method should be added. There are two scenarios: Phantom methods are disabled, and an error is thrown. Or phantom methods are enabled and these methods get added whenever Soot encounters a reference to a method that does not exist. More precisely, the phantom method gets added as soon as someone resolves the respective method reference (getMethod() on the reference in the InvokeExpr). Even if the bodies are loaded, that doesn't mean that all transitive references are resolved, it just means that the invocation is there, referencing some method that might or might not exist (to be resolved later).

StevenArzt avatar Jul 06 '22 20:07 StevenArzt