ART: Change merge(uninitialized, null) to produce conflict
The verifier would treat the result of merge(uninitialized, null) as
an uninitialized value, allowing a constructor to be called on null.
This patch conservatively prevents any usage of the merged register
by treating it as conflicting. Note that this could be relaxed with
a new common type for initialized and uninitialized references.
Bug: 26579108
Bug: 22411633
Change-Id: Ic5a1a3909c80287deece92dbb3254f437074e9b6
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 16cab03..0894f5d 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -667,13 +667,13 @@
// float/long/double MERGE float/long/double_constant => float/long/double
return SelectNonConstant(*this, incoming_type);
} else if (IsReferenceTypes() && incoming_type.IsReferenceTypes()) {
- if (IsZero() || incoming_type.IsZero()) {
- return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref
- } else if (IsUninitializedTypes() || incoming_type.IsUninitializedTypes()) {
+ if (IsUninitializedTypes() || incoming_type.IsUninitializedTypes()) {
// Something that is uninitialized hasn't had its constructor called. Unitialized types are
// special. They may only ever be merged with themselves (must be taken care of by the
// caller of Merge(), see the DCHECK on entry). So mark any other merge as conflicting here.
return conflict;
+ } else if (IsZero() || incoming_type.IsZero()) {
+ return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref
} else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) {
return reg_types->JavaLangObject(false); // Object MERGE ref => Object
} else if (IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) {
diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali
index 814cda5..cd52f3d 100644
--- a/test/563-checker-fakestring/smali/TestCase.smali
+++ b/test/563-checker-fakestring/smali/TestCase.smali
@@ -98,40 +98,3 @@
return v2
.end method
-
-# Test throwing and catching an exception between String's allocation and initialization.
-
-## CHECK-START: void TestCase.catchNewInstance() register (after)
-## CHECK-DAG: <<Null:l\d+>> NullConstant
-## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
-## CHECK-DAG: <<String:l\d+>> NewInstance
-## CHECK-DAG: <<UTF8:l\d+>> LoadString
-## CHECK-DAG: InvokeStaticOrDirect [<<Null>>,<<UTF8>>] env:[[<<String>>,<<Zero>>,<<UTF8>>]]
-## CHECK-DAG: Phi [<<Null>>,<<Null>>,<<String>>] reg:0 is_catch_phi:true
-
-.method public static catchNewInstance()V
- .registers 3
-
- const v0, 0x0
- :try_start
- new-instance v0, Ljava/lang/String;
-
- # Calling String.<init> on null byte array will throw NullPointerException
- # with v0 = new-instance.
- const v1, 0x0
- const-string v2, "UTF8"
- invoke-direct {v0, v1, v2}, Ljava/lang/String;-><init>([BLjava/lang/String;)V
- :try_end
- .catchall {:try_start .. :try_end} :catch
- return-void
-
- # Catch exception and test v0. Do not throw if it is not null.
- :catch
- move-exception v1
- if-nez v0, :return
- throw v1
-
- :return
- return-void
-
-.end method
diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java
index e0bfa7d..750f718 100644
--- a/test/563-checker-fakestring/src/Main.java
+++ b/test/563-checker-fakestring/src/Main.java
@@ -57,9 +57,5 @@
}
}
}
-
- {
- c.getMethod("catchNewInstance").invoke(null, (Object[]) null);
- }
}
}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 27f5b5d..2e66af5 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -49,4 +49,5 @@
b/25494456
b/21869691
b/26143249
+b/26579108
Done!
diff --git a/test/800-smali/smali/b_26579108.smali b/test/800-smali/smali/b_26579108.smali
new file mode 100644
index 0000000..dde3825
--- /dev/null
+++ b/test/800-smali/smali/b_26579108.smali
@@ -0,0 +1,34 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26579108;
+.super Ljava/lang/Object;
+
+# Ensure that merging uninitialized type and null does not pass verification.
+
+.field public static field:I
+
+.method public static run()Ljava/lang/String;
+ .registers 2
+ new-instance v0, Ljava/lang/String;
+
+ sget v1, LB26579108;->field:I
+ if-eqz v1, :cond_5
+
+ const/4 v0, 0x0
+ :cond_5
+
+ invoke-direct {v0}, Ljava/lang/String;-><init>()V
+ return-object v0
+ .end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index cc3b0b4..38aa58d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -143,6 +143,8 @@
new IncompatibleClassChangeError(), null));
testCases.add(new TestCase("b/26143249", "B26143249", "run", null,
new AbstractMethodError(), null));
+ testCases.add(new TestCase("b/26579108", "B26579108", "run", null, new VerifyError(),
+ null));
}
public void runTests() {
@@ -188,8 +190,7 @@
if (tc.expectedException != null) {
errorReturn = new IllegalStateException("Expected an exception in test " +
tc.testName);
- }
- if (tc.expectedReturn == null && retValue != null) {
+ } else if (tc.expectedReturn == null && retValue != null) {
errorReturn = new IllegalStateException("Expected a null result in test " +
tc.testName);
} else if (tc.expectedReturn != null &&