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 &&