Do not mutate reparse context fields until parsing is complete
This fixes a concurrency issue where concurrent parsing of two methods on the same class could lead to a NullPointerException:
1. Thread t0 wants to parse A.foo(). It therefore starts parsing class A.
2. Thread t0 finishes parsing of method A.bar(). As a side effect of this it sets the `CfCode code` field to the parsed code object and the `ReparseContext context` field to null.
3. Thread t1 wants to parse A.bar(). It sees that the `ReparseContext context` field is set to null and concludes that the method has already been parsed. The thread therefore continues.
4. Thread t0 starts parsing method A.foo() and finds that it contains a JSR instruction. It therefore aborts parsing and sets the `CfCode code` field to null and the `ReparseContext context` field to non-null for all code objects belonging to the class.
5. Thread t0 starts parsing class A from scratch with usrJsrInliner=true.
6. Thread t1 calls asCfCode() on the code object of A.bar(), since it assumes that the method has already been parsed. This method does not take the lock, but since the `CfCode code` field has been unset by thread t0 in step 4, thread t1 now starts parsing class A.
As a result both threads t0 and t1 are now parsing class A concurrently, which is not thread safe.
Bug: b/406073767
Change-Id: Ie8fe0096329e7d39598eaae0628ff6f819401acb
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 3618df3..4b5f81b 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -153,6 +153,8 @@
synchronized (context) {
asCfCode();
}
+ } else {
+ assert code != null;
}
}
@@ -173,8 +175,6 @@
} catch (JsrEncountered e) {
for (Code code : context.codeList) {
code.asLazyCfCode().code = null;
- code.asLazyCfCode().context = context;
- code.asLazyCfCode().application = application;
}
try {
parseCode(context, true, parsingOptions);
@@ -184,6 +184,11 @@
} catch (Exception e) {
throw new CompilationError("Could not parse code", e, origin);
}
+ for (Code code : context.codeList) {
+ assert code.asLazyCfCode().code != null;
+ code.asLazyCfCode().application = null;
+ code.asLazyCfCode().context = null;
+ }
assert verifyNoReparseContext(context.owner);
}
@@ -226,10 +231,9 @@
private void setCode(CfCode code) {
assert this.code == null;
+ assert this.application != null;
assert this.context != null;
this.code = code;
- this.context = null;
- this.application = null;
}
@Override