Handle a special case of lock aliasing during lock verification
Check whether the two instructions immediately before a monitor-enter
are const-class, establishing previously untracked lock aliasing
in a low-overhead manner.
Test: m test-art-host
Change-Id: I94c187cbc422f8f0c13b688b09a5d9579a735b56
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5961748..01fcbd1 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2255,6 +2255,45 @@
}
break;
+ // Catch a case of register aliasing when two registers are linked to the same
+ // java.lang.Class object via two consequent const-class instructions immediately
+ // preceding monitor-enter called on one of those registers.
+ case Instruction::CONST_CLASS: {
+ // Get the second previous instruction.
+ if (prev_idx == 0 || GetInstructionFlags(prev_idx).IsBranchTarget()) {
+ break;
+ }
+ prev_idx--;
+ while (0 != prev_idx && !GetInstructionFlags(prev_idx).IsOpcode()) {
+ prev_idx--;
+ }
+ const Instruction& prev2_inst = code_item_accessor_.InstructionAt(prev_idx);
+
+ // Match the pattern "const-class; const-class; monitor-enter;"
+ if (prev2_inst.Opcode() != Instruction::CONST_CLASS) {
+ break;
+ }
+
+ // Ensure both const-classes are called for the same type_idx.
+ if (prev_inst.VRegB_21c() != prev2_inst.VRegB_21c()) {
+ break;
+ }
+
+ // Update the lock status for the aliased register.
+ if (prev_inst.VRegA() == inst->VRegA_11x()) {
+ work_line_->CopyRegister1(this,
+ prev2_inst.VRegA(),
+ inst->VRegA_11x(),
+ kTypeCategoryRef);
+ } else if (prev2_inst.VRegA() == inst->VRegA_11x()) {
+ work_line_->CopyRegister1(this,
+ prev_inst.VRegA(),
+ inst->VRegA_11x(),
+ kTypeCategoryRef);
+ }
+ break;
+ }
+
default: // Other instruction types ignored.
break;
}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index b8324e5..f3c3f03 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -1,3 +1,4 @@
+JNI_OnLoad called
PackedSwitch
PackedSwitch key INT_MAX
PackedSwitch key overflow
@@ -71,4 +72,5 @@
b/29778499 (2)
b/30458218
b/31313170
+ConstClassAliasing
Done!
diff --git a/test/800-smali/smali/ConstClassAliasing.smali b/test/800-smali/smali/ConstClassAliasing.smali
new file mode 100644
index 0000000..a65d9a7
--- /dev/null
+++ b/test/800-smali/smali/ConstClassAliasing.smali
@@ -0,0 +1,12 @@
+.class public LConstClassAliasing;
+
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 2
+ const-class v0, Ljava/lang/Object;
+ const-class v1, Ljava/lang/Object;
+ monitor-enter v0
+ monitor-exit v1
+ return-void
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 8d39f09..75a0264 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -27,13 +27,21 @@
private static class TestCase {
public TestCase(String testName, String testClass, String testMethodName, Object[] values,
- Throwable expectedException, Object expectedReturn) {
+ Throwable expectedException, Object expectedReturn,
+ boolean checkCompiled) {
this.testName = testName;
this.testClass = testClass;
this.testMethodName = testMethodName;
this.values = values;
this.expectedException = expectedException;
this.expectedReturn = expectedReturn;
+ this.checkCompiled = checkCompiled;
+ }
+
+ public TestCase(String testName, String testClass, String testMethodName, Object[] values,
+ Throwable expectedException, Object expectedReturn) {
+ this(testName, testClass, testMethodName, values, expectedException,
+ expectedReturn, false);
}
String testName;
@@ -42,6 +50,7 @@
Object[] values;
Throwable expectedException;
Object expectedReturn;
+ boolean checkCompiled;
}
private List<TestCase> testCases;
@@ -182,6 +191,8 @@
new IncompatibleClassChangeError(), null));
testCases.add(new TestCase("b/30458218", "B30458218", "run", null, null, null));
testCases.add(new TestCase("b/31313170", "B31313170", "run", null, null, 0));
+ testCases.add(new TestCase("ConstClassAliasing", "ConstClassAliasing", "run", null, null,
+ null, true));
}
public void runTests() {
@@ -235,6 +246,11 @@
errorReturn = new IllegalStateException("Expected return " +
tc.expectedReturn +
", but got " + retValue);
+ } else if (tc.checkCompiled && compiledWithOptimizing() &&
+ !isAotCompiled(c, method.getName())) {
+ errorReturn = new IllegalStateException("Expected method " + method.getName() +
+ " of class " + c.getName() +
+ " be compiled in test " + tc.testName);
} else {
// Expected result, do nothing.
}
@@ -260,10 +276,15 @@
}
public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
Main main = new Main();
main.runTests();
System.out.println("Done!");
}
+
+ private native static boolean isAotCompiled(Class<?> cls, String methodName);
+ private native static boolean compiledWithOptimizing();
}
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index f89888b..8dabad9 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -132,9 +132,13 @@
constexpr const char* kInterpretOnly = "interpret-only";
constexpr const char* kVerifyNone = "verify-none";
constexpr const char* kVerifyAtRuntime = "verify-at-runtime";
+ constexpr const char* kQuicken = "quicken";
+ constexpr const char* kExtract = "extract";
if (strncmp(filter, kInterpretOnly, strlen(kInterpretOnly)) == 0 ||
strncmp(filter, kVerifyNone, strlen(kVerifyNone)) == 0 ||
- strncmp(filter, kVerifyAtRuntime, strlen(kVerifyAtRuntime)) == 0) {
+ strncmp(filter, kVerifyAtRuntime, strlen(kVerifyAtRuntime)) == 0 ||
+ strncmp(filter, kExtract, strlen(kExtract)) == 0 ||
+ strncmp(filter, kQuicken, strlen(kQuicken)) == 0) {
return JNI_FALSE;
}
}