Ensure the correctness of fast verify

We cannot guarantee that the resolution recorded in the vdex file is
correct when the boot classes are redefined. For example we might be doing
only sdk verification and not have the entire context available - as such,
if an app redefines a class that is in the boot classpath but not the sdk
we might record the wrong resolution. Another example is OTA time, when
the boot classpath may change.

The CL discards the vdex file when the app redefines classes from boot
classpath.

Test: test/testrunner/testrunner.py -t 719
Bug: 122968669
Change-Id: If0c56b1970d8ebe701d198ffccec52f586aea9e6
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e440eec..d46cffb 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1765,6 +1765,42 @@
   }
 }
 
+// Returns true if any of the given dex files define a class from the boot classpath.
+static bool DexFilesRedefineBootClasses(
+    const std::vector<const DexFile*>& dex_files,
+    TimingLogger* timings) {
+  TimingLogger::ScopedTiming t("Fast Verify: Boot Class Redefinition Check", timings);
+
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  bool foundRedefinition = false;
+  for (const DexFile* dex_file : dex_files) {
+    for (ClassAccessor accessor : dex_file->GetClasses()) {
+      const char* descriptor = accessor.GetDescriptor();
+      StackHandleScope<1> hs_class(self);
+      Handle<mirror::Class> klass =
+          hs_class.NewHandle(class_linker->FindSystemClass(self, descriptor));
+      if (klass == nullptr) {
+        self->ClearException();
+      } else {
+        LOG(WARNING) << "Redefinition of boot class " << descriptor
+            << " App dex file: " <<  accessor.GetDexFile().GetLocation()
+            << " Boot dex file: " << klass->GetDexFile().GetLocation();
+        foundRedefinition = true;
+        if (!VLOG_IS_ON(verifier)) {
+          // If we are not in verbose mode, return early.
+          // Otherwise continue and log all the collisions for easier debugging.
+          return true;
+        }
+      }
+    }
+  }
+
+  return foundRedefinition;
+}
+
 bool CompilerDriver::FastVerify(jobject jclass_loader,
                                 const std::vector<const DexFile*>& dex_files,
                                 TimingLogger* timings,
@@ -1776,6 +1812,17 @@
     return false;
   }
   TimingLogger::ScopedTiming t("Fast Verify", timings);
+
+  // We cannot do fast verification if the app redefines classes from the boot classpath.
+  // Vdex does not record resolution chains for boot classes and we might wrongfully
+  // resolve a class to the app when it should have been resolved to the boot classpath
+  // (e.g. if we verified against the SDK and the app redefines a boot class which is not
+  // in the SDK.)
+  if (DexFilesRedefineBootClasses(dex_files, timings)) {
+    LOG(WARNING) << "Found redefinition of boot classes. Not doing fast verification.";
+    return false;
+  }
+
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 35af918..808ad6c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1367,6 +1367,7 @@
           LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
         } else {
           input_vdex_file_ = std::make_unique<VdexFile>(std::move(input_file));
+          VLOG(verifier) << "Doing fast verification with vdex from DexMetadata archive";
         }
       }
     }
diff --git a/test/719-dm-verify-redefinition/check b/test/719-dm-verify-redefinition/check
new file mode 100644
index 0000000..b3c717c
--- /dev/null
+++ b/test/719-dm-verify-redefinition/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# Search for the redefinition line and remove unnecessary tags.
+sed -e 's/^dex2oat[d]\?\(\|32\|64\)\ W.*\] Found redefinition of boot classes\. Not doing fast verification\./Found redefinition of boot classes\. Not doing fast verification\./g' "$2" > "$2.tmp1"
+# Remove all other dex2oat log lines.
+grep -v dex2oat "$2.tmp1" >> "$2.tmp2"
+
+./default-check "$1" "$2.tmp2"
diff --git a/test/719-dm-verify-redefinition/expected.txt b/test/719-dm-verify-redefinition/expected.txt
new file mode 100644
index 0000000..64fb4ea
--- /dev/null
+++ b/test/719-dm-verify-redefinition/expected.txt
@@ -0,0 +1,3 @@
+Found redefinition of boot classes. Not doing fast verification.
+Hello, world!
+Correct resolution of boot class.
diff --git a/test/719-dm-verify-redefinition/info.txt b/test/719-dm-verify-redefinition/info.txt
new file mode 100644
index 0000000..1229bdb
--- /dev/null
+++ b/test/719-dm-verify-redefinition/info.txt
@@ -0,0 +1,2 @@
+Verifies that the vdex file from a DexMetadata archive is discarded
+if the app redefines boot classes.
diff --git a/test/719-dm-verify-redefinition/run b/test/719-dm-verify-redefinition/run
new file mode 100644
index 0000000..8e568b5
--- /dev/null
+++ b/test/719-dm-verify-redefinition/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+export ANDROID_LOG_TAGS='*:w'
+exec ${RUN} --external-log-tags --dm "${@}"
diff --git a/test/719-dm-verify-redefinition/src/Main.java b/test/719-dm-verify-redefinition/src/Main.java
new file mode 100644
index 0000000..37575b6
--- /dev/null
+++ b/test/719-dm-verify-redefinition/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+import java.util.BitSet;
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
+    if (BitSet.class.getClassLoader().equals(String.class.getClassLoader())) {
+      System.out.println("Correct resolution of boot class.");
+    } else {
+      System.out.println("Bogus resolution of boot class.");
+    }
+  }
+}
diff --git a/test/719-dm-verify-redefinition/src/java/util/BitSet.java b/test/719-dm-verify-redefinition/src/java/util/BitSet.java
new file mode 100644
index 0000000..5d91fd8
--- /dev/null
+++ b/test/719-dm-verify-redefinition/src/java/util/BitSet.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package java.util;
+
+public class BitSet {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index c4764e8..b97b4c1 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1177,5 +1177,13 @@
         "variant": "jit-on-first-use",
         "bug": "b/120112467",
         "description": [ "Fails on Android Build hosts with uncaught std::bad_alloc." ]
+    },
+    {
+        "tests": ["719-dm-verify-redefinition"],
+        "variant": "jvm | speed-profile | interp-ac | target",
+        "description": ["Doesn't run on RI because of boot class redefintion.",
+                        "Doesn't work with profiles because the run-test is not setup to",
+                        "support both. It also needs full verification, so no interp-ac.",
+                        "Requires zip, which isn't available on device"]
     }
 ]