Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConcurrentModificationException in SootClass.getMethodUnsafe #1116

Merged
merged 3 commits into from Aug 27, 2019

Conversation

jpstotz
Copy link
Contributor

@jpstotz jpstotz commented Feb 15, 2019

Using the latest Soot version I often encountered with certain apps a ConcurrentModificationException in
public SootMethod getMethodUnsafe(String name, List<Type> parameterTypes, Type returnType)

This pull request fixes that problem.
Additionally it includes a small non-related fix for a missing exception message in soot.jimple.toolkits.typing.integer.InternalTypingException which was always null instead of the actual message.

@@ -548,7 +548,7 @@ public SootMethod getMethodUnsafe(String name, List<Type> parameterTypes, Type r
return null;
}

for (SootMethod method : methodList) {
for (SootMethod method : new ArrayList<>(methodList)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a real fix, because the copy constructor of the ArrayList also iterates over the contents of the original list and copies over all elements. Therefore, if the list is concurrently modified by another thread, this issue might as well strike while the copy constructor is running. The much better question would be why the method is concurrently modified in the first place

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you ever encountered a ConcurrentModificationException in the new ArrayList(Collection) constructor? Or are you referring to that the ´methodList` variable is not synchronized?

I have not investigated why the method is concurrently modified, however I assume that this happens because of necessary phantom method references being added as they are used by the current SootClass instance or any other SootClass that is processed at that time in a different thread. I don't see a chance to synchronize the whole process.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, the copy constructor in newer JDKs doesn't iterate, but uses System.arrayCopy deep down. So you're technically right that you shouldn't get a ConcurrentModificationException anymore, but you might end up with all sorts of funny effects if a resizing happens while you are copying, because ArrayLists have a distinct field for their size, which they use when accessing the internal array. I think it's safe to say that your fix greatly reduces the risk of concurrency issues, but it's still a bit shady. I'm still not sure why aother thread manipulates methods concurrently here. Do you have a stack trace?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one example stack trace I found:

java.util.ConcurrentModificationException: null
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042) ~[na:na]
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996) ~[na:na]
	at soot.SootClass.getMethodUnsafe(SootClass.java:553) 
	at soot.SootMethodRefImpl.tryResolve(SootMethodRefImpl.java:210) 
	at soot.SootMethodRefImpl.tryResolve(SootMethodRefImpl.java:188) 
	at soot.toolkits.exceptions.UnitThrowAnalysis.mightThrow(UnitThrowAnalysis.java:297) 
	at soot.toolkits.exceptions.UnitThrowAnalysis$ValueSwitch.caseInstanceInvokeExpr(UnitThrowAnalysis.java:1120) 
	at soot.toolkits.exceptions.UnitThrowAnalysis$ValueSwitch.caseVirtualInvokeExpr(UnitThrowAnalysis.java:970) 
	at soot.jimple.internal.AbstractVirtualInvokeExpr.apply(AbstractVirtualInvokeExpr.java:79) 
	at soot.toolkits.exceptions.UnitThrowAnalysis.mightThrow(UnitThrowAnalysis.java:289) 
	at soot.toolkits.exceptions.UnitThrowAnalysis$UnitSwitch.caseAssignStmt(UnitThrowAnalysis.java:751) 
	at soot.jimple.internal.JAssignStmt.apply(JAssignStmt.java:242) 
	at soot.toolkits.exceptions.UnitThrowAnalysis.mightThrow(UnitThrowAnalysis.java:275) 
	at soot.toolkits.graph.ExceptionalUnitGraph.buildExceptionDests(ExceptionalUnitGraph.java:287) 
	at soot.toolkits.graph.ExceptionalUnitGraph.initialize(ExceptionalUnitGraph.java:229) 
	at soot.toolkits.graph.ExceptionalUnitGraph.<init>(ExceptionalUnitGraph.java:135) 
	at soot.toolkits.graph.ExceptionalUnitGraph.<init>(ExceptionalUnitGraph.java:163) 
	at soot.toolkits.scalar.LocalDefs$Factory.newLocalDefs(LocalDefs.java:65) 
	at soot.toolkits.scalar.LocalDefs$Factory.newLocalDefs(LocalDefs.java:51) 
	at soot.jimple.toolkits.scalar.DeadAssignmentEliminator.internalTransform(DeadAssignmentEliminator.java:239) 
	at soot.BodyTransformer.transform(BodyTransformer.java:55) 
	at soot.BodyTransformer.transform(BodyTransformer.java:59) 
	at soot.dexpler.DexBody.jimplify(DexBody.java:644) 
	at soot.dexpler.DexMethod$1.getBody(DexMethod.java:119) 
	at soot.SootMethod.retrieveActiveBody(SootMethod.java:402) 

It occurred when processing <cjh: void <init>(cja)> of com.starfinanz.smob.android.bwmobilbanking (one of several apps that caused this error).

I think more interesting would be where the call that modified the methodList came from, but that is not that simple to get.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do you call retrieveActiveBody()? Does that call stem from user code or from Soot code? Is that call issued in threaded code? I'm still trying to figure out where the multiple threads come from, i.e., why the method list gets changed while someone iterates over it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jpstotz @StevenArzt Can we come to a conclusion how to (not) handle this issue for now?
What do you think about my proposal?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question is whether we want to give any concurrency guarantees or not. I'd rather not, because there are tons of other places that are likely to fail as well. As I said, we created a concurrent version of Soot, but had to change the semantics of many operations. The only alternative would be to synchronize all accesses to internal collections as suggested by @mbenz89 , but that would pretty much lead to a single-threaded version again.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just fixed another issue that is caused by running the jimple body creation in parallel. There, the fast-hierarchy was not proper synchronized during jimple body creation.
@jpstotz is right here, PackManager does the exact parallel execution as he proposes.

I agree that synchronizing every field will ultimately lead to a kind-of single-threaded version again. However, the above-mentioned error seems to rise rather frequently, as it was reported by others as well.

I guess we have to options here:

  1. Disable the parallel jimplification at all, which will lead to huge performance decreases.
  2. At least synchronize the method list and see how this plays out, i.e. see which other race conditions we are witnessing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then let's do it like that, i.e., synchronize the method. It seems that we have no better option at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like using this copy-iteration approach proposed by Jan? And synchronizing the method list during the copy operation? Or are you saying we should synchronize the whole methodList?

BTW one of my students just found another synchronization issue, where multiple instances of the same class could be added to the Scene and have different resolving levels (causing #1078 and more). This can be omitted if we synchronize all operations that add/remove/check classes in Scene -.- . It's still far from single-threaded then, but it will definitely worsen performance quite a bit during jimplification, i guess.

@mbenz89
Copy link
Contributor

mbenz89 commented Mar 4, 2019

Might be related to #1083

@mbenz89
Copy link
Contributor

mbenz89 commented Mar 4, 2019

Might be related to #1067

@mbenz89
Copy link
Contributor

mbenz89 commented Jul 30, 2019

Might be related to #1162

@mbenz89 mbenz89 merged commit a49c756 into soot-oss:develop Aug 27, 2019
@xzf1234
Copy link

xzf1234 commented Dec 6, 2021

I found a similar error when processing the apk with soot
theakki.synctool.zip

the call stack is:

java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
	at java.util.ArrayList$Itr.next(ArrayList.java:861)
	at soot.FastHierarchy.getSignaturePolymorphicMethod(FastHierarchy.java:921)
	at soot.FastHierarchy.resolveMethod(FastHierarchy.java:822)
	at soot.FastHierarchy.resolveMethod(FastHierarchy.java:774)
	at soot.SootMethodRefImpl.tryResolve(SootMethodRefImpl.java:228)
	at soot.SootMethodRefImpl.tryResolve(SootMethodRefImpl.java:214)
	at soot.toolkits.exceptions.UnitThrowAnalysis.mightThrow(UnitThrowAnalysis.java:304)
	at soot.toolkits.exceptions.UnitThrowAnalysis$ValueSwitch.caseInstanceInvokeExpr(UnitThrowAnalysis.java:1181)
	at soot.toolkits.exceptions.UnitThrowAnalysis$ValueSwitch.caseInterfaceInvokeExpr(UnitThrowAnalysis.java:998)
	at soot.jimple.internal.AbstractInterfaceInvokeExpr.apply(AbstractInterfaceInvokeExpr.java:121)
	at soot.toolkits.exceptions.UnitThrowAnalysis.mightThrow(UnitThrowAnalysis.java:296)
	at soot.dexpler.DalvikThrowAnalysis.access$600(DalvikThrowAnalysis.java:155)
	at soot.dexpler.DalvikThrowAnalysis$1.caseAssignStmt(DalvikThrowAnalysis.java:228)
	at soot.jimple.internal.JAssignStmt.apply(JAssignStmt.java:209)
	at soot.toolkits.exceptions.UnitThrowAnalysis.mightThrow(UnitThrowAnalysis.java:280)
	at soot.toolkits.exceptions.UnitThrowAnalysis.mightThrow(UnitThrowAnalysis.java:275)
	at soot.toolkits.graph.ExceptionalUnitGraph.buildExceptionDests(ExceptionalUnitGraph.java:277)
	at soot.toolkits.graph.ExceptionalUnitGraph.initialize(ExceptionalUnitGraph.java:220)
	at soot.toolkits.graph.ExceptionalUnitGraph.<init>(ExceptionalUnitGraph.java:127)
	at soot.jimple.toolkits.scalar.UnreachableCodeEliminator.internalTransform(UnreachableCodeEliminator.java:86)
	at soot.BodyTransformer.transform(BodyTransformer.java:52)
	at soot.BodyTransformer.transform(BodyTransformer.java:56)
	at soot.dexpler.DexBody.jimplify(DexBody.java:697)
	at soot.dexpler.DexMethod$1.getBody(DexMethod.java:120)
	at soot.SootMethod.retrieveActiveBody(SootMethod.java:446)
	at soot.PackManager.lambda$retrieveAllBodies$2(PackManager.java:1252)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

I fixed the bug by adding new ArrayList<> in soot.FastHierarchy.getSignaturePolymorphicMethod like:

@@ -929,7 +918,7 @@
   private SootMethod getSignaturePolymorphicMethod(SootClass concreteType, String name, List<Type> parameterTypes,
       Type returnType) {
     SootMethod candidate = null;
-    for (SootMethod method : concreteType.getMethods()) {
+    for (SootMethod method : new ArrayList<>( concreteType.getMethods())) {

@jpstotz jpstotz deleted the fixes branch December 6, 2021 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants