diff options
author | 2019-03-01 22:25:31 +0000 | |
---|---|---|
committer | 2019-04-01 15:54:39 +0000 | |
commit | d592dbe1e2b09079382713220b2ff59f02db1859 (patch) | |
tree | 0782c75d713200b1e99dee9f97b4898612b3fee3 | |
parent | 0a20607d671bf001043ccb0cf156db16b4fc6bed (diff) |
Verify InMemoryDexClassLoader classes in a background thread
When dex bytecode is loaded using InMemoryDexClassLoader, automatically
spawn a background thread which performs bytecode verification on every
class.
Bug: 72131483
Test: art/tools/run-libcore-tests.sh
Test: art/test.py -b -r -t 692
Change-Id: Iad54f510de02cd073e68d775d34b7dd5bdef304e
-rw-r--r-- | runtime/native/dalvik_system_DexFile.cc | 21 | ||||
-rw-r--r-- | runtime/oat_file_manager.cc | 109 | ||||
-rw-r--r-- | runtime/oat_file_manager.h | 18 | ||||
-rw-r--r-- | runtime/runtime.cc | 11 | ||||
-rw-r--r-- | test/674-hiddenapi/src-art/Main.java | 27 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/expected.txt | 3 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/info.txt | 3 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/src-ex/DummyClass.java | 18 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/src-secondary/art/ClassA.java | 23 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/src-secondary/art/ClassB.java | 23 | ||||
-rwxr-xr-x | test/692-vdex-inmem-loader/src-secondary/gen.sh | 37 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/src/Main.java | 108 | ||||
-rw-r--r-- | test/692-vdex-inmem-loader/vdex_inmem_loader.cc | 72 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rw-r--r-- | test/knownfailures.json | 2 |
15 files changed, 450 insertions, 26 deletions
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index feaf619afa..d1ea6559ad 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -340,6 +340,26 @@ static jobject DexFile_openDexFileNative(JNIEnv* env, } } +static void DexFile_verifyInBackgroundNative(JNIEnv* env, + jclass, + jobject cookie, + jobject class_loader) { + CHECK(cookie != nullptr); + CHECK(class_loader != nullptr); + + // Extract list of dex files from the cookie. + std::vector<const DexFile*> dex_files; + const OatFile* oat_file; + if (!ConvertJavaArrayToDexFiles(env, cookie, dex_files, oat_file)) { + Thread::Current()->AssertPendingException(); + return; + } + CHECK(oat_file == nullptr) << "Called verifyInBackground on a dex file backed by oat"; + + // Hand over to OatFileManager to spawn a verification thread. + Runtime::Current()->GetOatFileManager().RunBackgroundVerification(dex_files, class_loader); +} + static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) { std::vector<const DexFile*> dex_files; const OatFile* oat_file; @@ -893,6 +913,7 @@ static JNINativeMethod gMethods[] = { "[I" "[I" ")Ljava/lang/Object;"), + NATIVE_METHOD(DexFile, verifyInBackgroundNative, "(Ljava/lang/Object;Ljava/lang/ClassLoader;)V"), NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"), NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"), NATIVE_METHOD(DexFile, diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index b03551b995..47a6f997a4 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -39,6 +39,7 @@ #include "gc/scoped_gc_critical_section.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" +#include "jni/java_vm_ext.h" #include "jni/jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" @@ -48,6 +49,7 @@ #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" +#include "thread_pool.h" #include "well_known_classes.h" namespace art { @@ -642,6 +644,113 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( return dex_files; } +class BackgroundVerificationTask final : public Task { + public: + BackgroundVerificationTask(const std::vector<const DexFile*>& dex_files, jobject class_loader) + : dex_files_(dex_files) { + Thread* const self = Thread::Current(); + ScopedObjectAccess soa(self); + // Create a global ref for `class_loader` because it will be accessed from a different thread. + class_loader_ = soa.Vm()->AddGlobalRef(self, soa.Decode<mirror::ClassLoader>(class_loader)); + CHECK(class_loader_ != nullptr); + } + + ~BackgroundVerificationTask() { + Thread* const self = Thread::Current(); + ScopedObjectAccess soa(self); + soa.Vm()->DeleteGlobalRef(self, class_loader_); + } + + void Run(Thread* self) override { + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + + // Iterate over all classes and verify them. + for (const DexFile* dex_file : dex_files_) { + for (uint32_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); cdef_idx++) { + // Take handles inside the loop. The background verification is low priority + // and we want to minimize the risk of blocking anyone else. + ScopedObjectAccess soa(self); + StackHandleScope<2> hs(self); + Handle<mirror::ClassLoader> h_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader>(class_loader_))); + Handle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(class_linker->FindClass( + self, + dex_file->GetClassDescriptor(dex_file->GetClassDef(cdef_idx)), + h_loader))); + + if (h_class == nullptr) { + CHECK(self->IsExceptionPending()); + self->ClearException(); + continue; + } + + if (&h_class->GetDexFile() != dex_file) { + // There is a different class in the class path or a parent class loader + // with the same descriptor. This `h_class` is not resolvable, skip it. + continue; + } + + CHECK(h_class->IsResolved()) << h_class->PrettyDescriptor(); + class_linker->VerifyClass(self, h_class); + if (h_class->IsErroneous()) { + // ClassLinker::VerifyClass throws, which isn't useful here. + CHECK(soa.Self()->IsExceptionPending()); + soa.Self()->ClearException(); + } + + CHECK(h_class->IsVerified() || h_class->IsErroneous()) + << h_class->PrettyDescriptor() << ": state=" << h_class->GetStatus(); + } + } + } + + void Finalize() override { + delete this; + } + + private: + const std::vector<const DexFile*> dex_files_; + jobject class_loader_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundVerificationTask); +}; + +void OatFileManager::RunBackgroundVerification(const std::vector<const DexFile*>& dex_files, + jobject class_loader) { + Thread* const self = Thread::Current(); + if (Runtime::Current()->IsShuttingDown(self)) { + // Not allowed to create new threads during runtime shutdown. + return; + } + + if (verification_thread_pool_ == nullptr) { + verification_thread_pool_.reset(new ThreadPool("Verification thread pool", + /* num_threads= */ 1)); + verification_thread_pool_->StartWorkers(self); + } + + verification_thread_pool_->AddTask(self, new BackgroundVerificationTask(dex_files, class_loader)); +} + +void OatFileManager::WaitForWorkersToBeCreated() { + DCHECK(!Runtime::Current()->IsShuttingDown(Thread::Current())) + << "Cannot create new threads during runtime shutdown"; + if (verification_thread_pool_ != nullptr) { + verification_thread_pool_->WaitForWorkersToBeCreated(); + } +} + +void OatFileManager::DeleteThreadPool() { + verification_thread_pool_.reset(nullptr); +} + +void OatFileManager::WaitForBackgroundVerificationTasks() { + Thread* const self = Thread::Current(); + if (verification_thread_pool_ != nullptr) { + verification_thread_pool_->Wait(self, /* do_work= */ true, /* may_hold_locks= */ false); + } +} + void OatFileManager::SetOnlyUseSystemOatFiles(bool assert_no_files_loaded) { ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); if (assert_no_files_loaded) { diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 9c6c04bc91..24d8e42fd0 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -38,6 +38,7 @@ class ImageSpace; class ClassLoaderContext; class DexFile; class OatFile; +class ThreadPool; // Class for dealing with oat file management. // @@ -103,6 +104,20 @@ class OatFileManager { void SetOnlyUseSystemOatFiles(bool assert_no_files_loaded); + // Spawn a background thread which verifies all classes in the given dex files. + void RunBackgroundVerification(const std::vector<const DexFile*>& dex_files, + jobject class_loader); + + // Wait for thread pool workers to be created. This is used during shutdown as + // threads are not allowed to attach while runtime is in shutdown lock. + void WaitForWorkersToBeCreated(); + + // If allocated, delete a thread pool of background verification threads. + void DeleteThreadPool(); + + // Wait for all background verification tasks to finish. This is only used by tests. + void WaitForBackgroundVerificationTasks(); + private: enum class CheckCollisionResult { kSkippedUnsupportedClassLoader, @@ -143,6 +158,9 @@ class OatFileManager { // is not on /system, don't load it "executable". bool only_use_system_oat_files_; + // Single-thread pool used to run the verifier in the background. + std::unique_ptr<ThreadPool> verification_thread_pool_; + DISALLOW_COPY_AND_ASSIGN(OatFileManager); }; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index c978b83a85..c4e4b6142e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -364,17 +364,19 @@ Runtime::~Runtime() { << "\n"; } + // Wait for the workers of thread pools to be created since there can't be any + // threads attaching during shutdown. WaitForThreadPoolWorkersToStart(); - if (jit_ != nullptr) { - // Wait for the workers to be created since there can't be any threads attaching during - // shutdown. jit_->WaitForWorkersToBeCreated(); // Stop the profile saver thread before marking the runtime as shutting down. // The saver will try to dump the profiles before being sopped and that // requires holding the mutator lock. jit_->StopProfileSaver(); } + if (oat_file_manager_ != nullptr) { + oat_file_manager_->WaitForWorkersToBeCreated(); + } { ScopedTrace trace2("Wait for shutdown cond"); @@ -419,6 +421,9 @@ Runtime::~Runtime() { // JIT compiler threads. jit_->DeleteThreadPool(); } + if (oat_file_manager_ != nullptr) { + oat_file_manager_->DeleteThreadPool(); + } DeleteThreadPool(); CHECK(thread_pool_ == nullptr); diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java index d6a8c6dbd8..c92d3527af 100644 --- a/test/674-hiddenapi/src-art/Main.java +++ b/test/674-hiddenapi/src-art/Main.java @@ -14,18 +14,12 @@ * limitations under the License. */ -import dalvik.system.InMemoryDexClassLoader; import dalvik.system.PathClassLoader; -import dalvik.system.VMRuntime; import java.io.File; -import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.nio.ByteBuffer; import java.nio.file.Files; import java.util.Arrays; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; public class Main { // This needs to be kept in sync with DexDomain in ChildClass. @@ -52,6 +46,9 @@ public class Main { // As a side effect, we also cannot test Platform->Platform and later // Platform->CorePlatform as the former succeeds in verifying linkage usages // that should fail in the latter. + // We also cannot use InMemoryDexClassLoader because it runs verification in + // a background thread and being able to dynamically change the configuration + // (like list of exemptions) would require proper thread synchronization. // Run test with both parent and child dex files loaded with class loaders. // The expectation is that hidden members in parent should be visible to @@ -104,7 +101,7 @@ public class Main { // Load child dex if it is not in boot class path. ClassLoader childLoader = null; if (childDomain == DexDomain.Application) { - childLoader = new InMemoryDexClassLoader(readDexFile(DEX_CHILD), parentLoader); + childLoader = new PathClassLoader(DEX_CHILD, parentLoader); } else { if (parentLoader != BOOT_CLASS_LOADER) { throw new IllegalStateException( @@ -150,22 +147,6 @@ public class Main { } } - // Helper to read dex file into memory. - private static ByteBuffer readDexFile(String jarFileName) throws Exception { - ZipFile zip = new ZipFile(new File(jarFileName)); - ZipEntry entry = zip.getEntry("classes.dex"); - InputStream is = zip.getInputStream(entry); - int offset = 0; - int size = (int) entry.getSize(); - ByteBuffer buffer = ByteBuffer.allocate(size); - while (is.available() > 0) { - is.read(buffer.array(), offset, size - offset); - } - is.close(); - zip.close(); - return buffer; - } - // Copy native library to a new file with a unique name so it does not // conflict with other loaded instance of the same binary file. private static String createNativeLibCopy(DexDomain parentDomain, DexDomain childDomain, diff --git a/test/692-vdex-inmem-loader/expected.txt b/test/692-vdex-inmem-loader/expected.txt new file mode 100644 index 0000000000..0990d72f3d --- /dev/null +++ b/test/692-vdex-inmem-loader/expected.txt @@ -0,0 +1,3 @@ +JNI_OnLoad called +Hello +Hello diff --git a/test/692-vdex-inmem-loader/info.txt b/test/692-vdex-inmem-loader/info.txt new file mode 100644 index 0000000000..435b9c5b62 --- /dev/null +++ b/test/692-vdex-inmem-loader/info.txt @@ -0,0 +1,3 @@ +Test that dex files loaded with InMemoryDexClassLoader get verified and the verification results +cached in a vdex file in the app's data folder. Subsequent loads should initialize an instance of +OatFile using the data in the vdex.
\ No newline at end of file diff --git a/test/692-vdex-inmem-loader/src-ex/DummyClass.java b/test/692-vdex-inmem-loader/src-ex/DummyClass.java new file mode 100644 index 0000000000..443d1fe158 --- /dev/null +++ b/test/692-vdex-inmem-loader/src-ex/DummyClass.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +public class DummyClass { +} diff --git a/test/692-vdex-inmem-loader/src-secondary/art/ClassA.java b/test/692-vdex-inmem-loader/src-secondary/art/ClassA.java new file mode 100644 index 0000000000..2b2bc5d5b3 --- /dev/null +++ b/test/692-vdex-inmem-loader/src-secondary/art/ClassA.java @@ -0,0 +1,23 @@ +/* + * 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 art; + +public class ClassA { + public static String getHello() { + return "Hello"; + } +} diff --git a/test/692-vdex-inmem-loader/src-secondary/art/ClassB.java b/test/692-vdex-inmem-loader/src-secondary/art/ClassB.java new file mode 100644 index 0000000000..45c450eaf5 --- /dev/null +++ b/test/692-vdex-inmem-loader/src-secondary/art/ClassB.java @@ -0,0 +1,23 @@ +/* + * 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 art; + +public class ClassB { + public static void printHello() { + System.out.println(ClassA.getHello()); + } +} diff --git a/test/692-vdex-inmem-loader/src-secondary/gen.sh b/test/692-vdex-inmem-loader/src-secondary/gen.sh new file mode 100755 index 0000000000..67df40e8c5 --- /dev/null +++ b/test/692-vdex-inmem-loader/src-secondary/gen.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Copyright 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. + +set -e +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TMP=`mktemp -d` + +CLASS_A="art/ClassA" +CLASS_B="art/ClassB" + +(cd "$TMP" && \ + javac -d "${TMP}" "$DIR/${CLASS_A}.java" "$DIR/${CLASS_B}.java" && \ + d8 --output . "$TMP/${CLASS_A}.class" && + mv "$TMP/classes.dex" "$TMP/classesA.dex" && + d8 --output . "$TMP/${CLASS_B}.class" && + mv "$TMP/classes.dex" "$TMP/classesB.dex") + +echo ' private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode(' +base64 "${TMP}/classesA.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' + +echo ' private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode(' +base64 "${TMP}/classesB.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' + +rm -rf "$TMP" diff --git a/test/692-vdex-inmem-loader/src/Main.java b/test/692-vdex-inmem-loader/src/Main.java new file mode 100644 index 0000000000..bfdf16aa1e --- /dev/null +++ b/test/692-vdex-inmem-loader/src/Main.java @@ -0,0 +1,108 @@ +/* + * 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 dalvik.system.InMemoryDexClassLoader; +import java.lang.reflect.Method; +import java.io.File; +import java.nio.ByteBuffer; +import java.util.Base64; + +public class Main { + private static void check(boolean expected, boolean actual, String message) { + if (expected != actual) { + System.err.println( + "ERROR: " + message + " (expected=" + expected + ", actual=" + actual + ")"); + } + } + + private static ClassLoader singleLoader() { + return new InMemoryDexClassLoader( + new ByteBuffer[] { ByteBuffer.wrap(DEX_BYTES_A), ByteBuffer.wrap(DEX_BYTES_B) }, + /*parent*/null); + } + + private static ClassLoader[] multiLoader() { + ClassLoader clA = new InMemoryDexClassLoader(ByteBuffer.wrap(DEX_BYTES_A), /*parent*/ null); + ClassLoader clB = new InMemoryDexClassLoader(ByteBuffer.wrap(DEX_BYTES_B), /*parent*/ clA); + return new ClassLoader[] { clA, clB }; + } + + private static void test(ClassLoader loader, boolean invokeMethod) throws Exception { + waitForVerifier(); + check(true, areClassesVerified(loader), "areClassesVerified"); + + if (invokeMethod) { + loader.loadClass("art.ClassB").getDeclaredMethod("printHello").invoke(null); + } + } + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + ClassLoader[] loaders = null; + + // Test loading both dex files in a single class loader. + // Background verification task should verify all their classes. + test(singleLoader(), /*invokeMethod*/true); + + // Test loading the two dex files with separate class loaders. + // Background verification task should still verify all classes. + loaders = multiLoader(); + test(loaders[0], /*invokeMethod*/false); + test(loaders[1], /*invokeMethod*/true); + } + + private static native void waitForVerifier(); + private static native boolean areClassesVerified(ClassLoader loader); + + // Defined in 674-hiddenapi. + private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform); + + private static final String DEX_LOCATION = System.getenv("DEX_LOCATION"); + private static final String DEX_EXTRA = + new File(DEX_LOCATION, "692-vdex-inmem-loader-ex.jar").getAbsolutePath(); + + private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode( + "ZGV4CjAzNQBxYu/tdPfiHaRPYr5yaT6ko9V/xMinr1OwAgAAcAAAAHhWNBIAAAAAAAAAABwCAAAK" + + "AAAAcAAAAAQAAACYAAAAAgAAAKgAAAAAAAAAAAAAAAMAAADAAAAAAQAAANgAAAC4AQAA+AAAADAB" + + "AAA4AQAARQEAAEwBAABPAQAAXQEAAHEBAACFAQAAiAEAAJIBAAAEAAAABQAAAAYAAAAHAAAAAwAA" + + "AAIAAAAAAAAABwAAAAMAAAAAAAAAAAABAAAAAAAAAAAACAAAAAEAAQAAAAAAAAAAAAEAAAABAAAA" + + "AAAAAAEAAAAAAAAACQIAAAAAAAABAAAAAAAAACwBAAADAAAAGgACABEAAAABAAEAAQAAACgBAAAE" + + "AAAAcBACAAAADgATAA4AFQAOAAY8aW5pdD4AC0NsYXNzQS5qYXZhAAVIZWxsbwABTAAMTGFydC9D" + + "bGFzc0E7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwABVgAIZ2V0SGVs" + + "bG8AdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoi" + + "OTY2MDhmZDdiYmNjZGQyMjc2Y2Y4OTI4M2QyYjgwY2JmYzRmYzgxYyIsInZlcnNpb24iOiIxLjUu" + + "NC1kZXYifQAAAAIAAIGABJACAQn4AQAAAAAADAAAAAAAAAABAAAAAAAAAAEAAAAKAAAAcAAAAAIA" + + "AAAEAAAAmAAAAAMAAAACAAAAqAAAAAUAAAADAAAAwAAAAAYAAAABAAAA2AAAAAEgAAACAAAA+AAA" + + "AAMgAAACAAAAKAEAAAIgAAAKAAAAMAEAAAAgAAABAAAACQIAAAMQAAABAAAAGAIAAAAQAAABAAAA" + + "HAIAAA=="); + private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode( + "ZGV4CjAzNQB+hWvce73hXt7ZVNgp9RAyMLSwQzsWUjV4AwAAcAAAAHhWNBIAAAAAAAAAAMwCAAAQ" + + "AAAAcAAAAAcAAACwAAAAAwAAAMwAAAABAAAA8AAAAAUAAAD4AAAAAQAAACABAAA4AgAAQAEAAI4B" + + "AACWAQAAowEAAKYBAAC0AQAAwgEAANkBAADtAQAAAQIAABUCAAAYAgAAHAIAACYCAAArAgAANwIA" + + "AEACAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAAAgAAAAQAAAAAAAAACQAAAAYAAAAAAAAA" + + "CgAAAAYAAACIAQAABQACAAwAAAAAAAAACwAAAAEAAQAAAAAAAQABAA0AAAACAAIADgAAAAMAAQAA" + + "AAAAAQAAAAEAAAADAAAAAAAAAAEAAAAAAAAAtwIAAAAAAAABAAEAAQAAAHwBAAAEAAAAcBAEAAAA" + + "DgACAAAAAgAAAIABAAAKAAAAYgAAAHEAAAAAAAwBbiADABAADgATAA4AFQAOlgAAAAABAAAABAAG" + + "PGluaXQ+AAtDbGFzc0IuamF2YQABTAAMTGFydC9DbGFzc0E7AAxMYXJ0L0NsYXNzQjsAFUxqYXZh" + + "L2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" + + "EkxqYXZhL2xhbmcvU3lzdGVtOwABVgACVkwACGdldEhlbGxvAANvdXQACnByaW50SGVsbG8AB3By" + + "aW50bG4AdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0x" + + "IjoiOTY2MDhmZDdiYmNjZGQyMjc2Y2Y4OTI4M2QyYjgwY2JmYzRmYzgxYyIsInZlcnNpb24iOiIx" + + "LjUuNC1kZXYifQAAAAIAAYGABMACAQnYAgAAAAAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABw" + + "AAAAAgAAAAcAAACwAAAAAwAAAAMAAADMAAAABAAAAAEAAADwAAAABQAAAAUAAAD4AAAABgAAAAEA" + + "AAAgAQAAASAAAAIAAABAAQAAAyAAAAIAAAB8AQAAARAAAAEAAACIAQAAAiAAABAAAACOAQAAACAA" + + "AAEAAAC3AgAAAxAAAAEAAADIAgAAABAAAAEAAADMAgAA"); +} diff --git a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc new file mode 100644 index 0000000000..f064953a29 --- /dev/null +++ b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 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. + */ + +#include "class_loader_utils.h" +#include "jni.h" +#include "nativehelper/scoped_utf_chars.h" +#include "oat_file_assistant.h" +#include "oat_file_manager.h" +#include "scoped_thread_state_change-inl.h" +#include "thread.h" + +namespace art { +namespace Test692VdexInmemLoader { + +extern "C" JNIEXPORT void JNICALL Java_Main_waitForVerifier(JNIEnv*, jclass) { + Runtime::Current()->GetOatFileManager().WaitForBackgroundVerificationTasks(); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_areClassesVerified(JNIEnv*, + jclass, + jobject loader) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + Handle<mirror::ClassLoader> h_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader))); + + std::vector<const DexFile*> dex_files; + VisitClassLoaderDexFiles( + soa, + h_loader, + [&](const DexFile* dex_file) { + dex_files.push_back(dex_file); + return true; + }); + + MutableHandle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr)); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + + bool is_first = true; + bool all_verified = false; + for (const DexFile* dex_file : dex_files) { + for (uint16_t cdef_idx = 0; cdef_idx < dex_file->NumClassDefs(); ++cdef_idx) { + const char* desc = dex_file->GetClassDescriptor(dex_file->GetClassDef(cdef_idx)); + h_class.Assign(class_linker->FindClass(soa.Self(), desc, h_loader)); + CHECK(h_class != nullptr) << "Could not find class " << desc; + bool is_verified = h_class->IsVerified(); + if (is_first) { + all_verified = is_verified; + is_first = false; + } else if (all_verified != is_verified) { + // Classes should either all or none be verified. + LOG(ERROR) << "areClassesVerified is inconsistent"; + } + } + } + return all_verified ? JNI_TRUE : JNI_FALSE; +} + +} // namespace Test692VdexInmemLoader +} // namespace art diff --git a/test/Android.bp b/test/Android.bp index 5460b3a34c..3e56442724 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -492,6 +492,7 @@ cc_defaults { "664-aget-verifier/aget-verifier.cc", "667-jit-jni-stub/jit_jni_stub_test.cc", "674-hiddenapi/hiddenapi.cc", + "692-vdex-inmem-loader/vdex_inmem_loader.cc", "708-jit-cache-churn/jit.cc", "800-smali/jni.cc", "909-attach-agent/disallow_debugging.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index e78e0028d0..f77999be4b 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -559,6 +559,7 @@ "674-hiddenapi", "690-hiddenapi-same-name-methods", "691-hiddenapi-proxy", + "692-vdex-inmem-loader", "944-transform-classloaders", "999-redefine-hiddenapi" ], @@ -1094,6 +1095,7 @@ "688-shared-library", "690-hiddenapi-same-name-methods", "691-hiddenapi-proxy", + "692-vdex-inmem-loader", "999-redefine-hiddenapi", "1000-non-moving-space-stress", "1001-app-image-regions", |