diff options
| author | 2020-06-02 07:13:00 +0000 | |
|---|---|---|
| committer | 2020-06-02 07:13:00 +0000 | |
| commit | c57334a08e04ebdec7919e604b05e7fa2ceb9a05 (patch) | |
| tree | fb6abb08148000f9e1b59aead2ff4b27dd9bcb72 | |
| parent | 786170d33819d7e6f5c063503ce3751ac04fc1d8 (diff) | |
| parent | 0d8a211fa279fca639434b3a4ec987adaaa1e664 (diff) | |
Snap for 6549967 from 0d8a211fa279fca639434b3a4ec987adaaa1e664 to mainline-release
Change-Id: Iaa4badbbaa8c846eb11385a969a02043fc355872
| -rw-r--r-- | dexlayout/dex_writer.cc | 7 | ||||
| -rw-r--r-- | libdexfile/dex/standard_dex_file.cc | 9 | ||||
| -rw-r--r-- | libdexfile/dex/standard_dex_file.h | 4 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 13 | ||||
| -rw-r--r-- | runtime/mirror/class-inl.h | 6 | ||||
| -rw-r--r-- | runtime/mirror/dex_cache-inl.h | 1 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_VMRuntime.cc | 10 | ||||
| -rw-r--r-- | test/180-native-default-method/build | 30 | ||||
| -rw-r--r-- | test/180-native-default-method/expected.txt | 1 | ||||
| -rw-r--r-- | test/180-native-default-method/info.txt | 3 | ||||
| -rw-r--r-- | test/180-native-default-method/jasmin/TestClass.j | 25 | ||||
| -rw-r--r-- | test/180-native-default-method/jasmin/TestInterface.j | 19 | ||||
| -rw-r--r-- | test/180-native-default-method/src/Main.java | 32 |
13 files changed, 144 insertions, 16 deletions
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 268abe4415..7f05ae89fb 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -794,8 +794,11 @@ void DexWriter::WriteHeader(Stream* stream) { StandardDexFile::Header header; if (CompactDexFile::IsMagicValid(header_->Magic())) { StandardDexFile::WriteMagic(header.magic_); - // TODO: Should we write older versions based on the feature flags? - StandardDexFile::WriteCurrentVersion(header.magic_); + if (header_->SupportDefaultMethods()) { + StandardDexFile::WriteCurrentVersion(header.magic_); + } else { + StandardDexFile::WriteVersionBeforeDefaultMethods(header.magic_); + } } else { // Standard dex -> standard dex, just reuse the same header. static constexpr size_t kMagicAndVersionLen = diff --git a/libdexfile/dex/standard_dex_file.cc b/libdexfile/dex/standard_dex_file.cc index 9c4cb45f8b..1f1bc19ae2 100644 --- a/libdexfile/dex/standard_dex_file.cc +++ b/libdexfile/dex/standard_dex_file.cc @@ -32,9 +32,9 @@ const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersion {'0', '3', '7', '\0'}, // Dex version 038: Android "O" and beyond. {'0', '3', '8', '\0'}, - // Dex verion 039: Android "P" and beyond. + // Dex version 039: Android "P" and beyond. {'0', '3', '9', '\0'}, - // Dex verion 040: beyond Android "10" (previously known as Android "Q"). + // Dex version 040: beyond Android "10" (previously known as Android "Q"). {'0', '4', '0', '\0'}, }; @@ -48,6 +48,11 @@ void StandardDexFile::WriteCurrentVersion(uint8_t* magic) { magic + kDexMagicSize); } + +void StandardDexFile::WriteVersionBeforeDefaultMethods(uint8_t* magic) { + std::copy_n(kDexMagicVersions[0u], kDexVersionLen, magic + kDexMagicSize); +} + bool StandardDexFile::IsMagicValid(const uint8_t* magic) { return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); } diff --git a/libdexfile/dex/standard_dex_file.h b/libdexfile/dex/standard_dex_file.h index 3af36f6791..25cf62a417 100644 --- a/libdexfile/dex/standard_dex_file.h +++ b/libdexfile/dex/standard_dex_file.h @@ -83,6 +83,10 @@ class StandardDexFile : public DexFile { // Write the current version, note that the input is the address of the magic. static void WriteCurrentVersion(uint8_t* magic); + // Write the last version before default method support, + // note that the input is the address of the magic. + static void WriteVersionBeforeDefaultMethods(uint8_t* magic); + static const uint8_t kDexMagic[kDexMagicSize]; static constexpr size_t kNumDexVersions = 5; static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen]; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 6550c9bb40..cf2eb0312f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -6423,6 +6423,19 @@ bool ClassLinker::LinkVirtualMethods( ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_); m->SetMethodIndex(i); if (!m->IsAbstract()) { + // If the dex file does not support default methods, throw ClassFormatError. + // This check is necessary to protect from odd cases, such as native default + // methods, that the dex file verifier permits for old dex file versions. b/157170505 + // FIXME: This should be `if (!m->GetDexFile()->SupportsDefaultMethods())` but we're + // currently running CTS tests for default methods with dex file version 035 which + // does not support default methods. So, we limit this to native methods. b/157718952 + if (m->IsNative()) { + DCHECK(!m->GetDexFile()->SupportsDefaultMethods()); + ThrowClassFormatError(klass.Get(), + "Dex file does not support default method '%s'", + m->PrettyMethod().c_str()); + return false; + } m->SetAccessFlags(m->GetAccessFlags() | kAccDefault); has_defaults = true; } diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index ef82d2345e..6a5317c1b6 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -131,7 +131,7 @@ inline uint32_t Class::GetVirtualMethodsStartOffset() { template<VerifyObjectFlags kVerifyFlags> inline ArraySlice<ArtMethod> Class::GetDirectMethodsSlice(PointerSize pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); + DCHECK(IsLoaded() || IsErroneous()) << GetStatus(); return GetDirectMethodsSliceUnchecked(pointer_size); } @@ -144,7 +144,7 @@ inline ArraySlice<ArtMethod> Class::GetDirectMethodsSliceUnchecked(PointerSize p template<VerifyObjectFlags kVerifyFlags> inline ArraySlice<ArtMethod> Class::GetDeclaredMethodsSlice(PointerSize pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); + DCHECK(IsLoaded() || IsErroneous()) << GetStatus(); return GetDeclaredMethodsSliceUnchecked(pointer_size); } @@ -157,7 +157,7 @@ inline ArraySlice<ArtMethod> Class::GetDeclaredMethodsSliceUnchecked(PointerSize template<VerifyObjectFlags kVerifyFlags> inline ArraySlice<ArtMethod> Class::GetDeclaredVirtualMethodsSlice(PointerSize pointer_size) { - DCHECK(IsLoaded() || IsErroneous()); + DCHECK(IsLoaded() || IsErroneous()) << GetStatus(); return GetDeclaredVirtualMethodsSliceUnchecked(pointer_size); } diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 4f23273065..d5e1362ada 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -164,6 +164,7 @@ inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) { inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) { DCHECK(resolved != nullptr); + DCHECK(resolved->IsResolved()) << resolved->GetStatus(); // TODO default transaction support. // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a // class but not necessarily seeing the loaded members like the static fields array. diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index bb3ff117bd..efaa3d938a 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -399,7 +399,6 @@ static void PreloadDexCachesResolveString( if (string == nullptr) { return; } - // LOG(INFO) << "VMRuntime.preloadDexCaches resolved string=" << utf8; dex_cache->SetResolvedString(string_idx, string); } @@ -419,17 +418,10 @@ static void PreloadDexCachesResolveType(Thread* self, ObjPtr<mirror::Class> klass = (class_name[1] == '\0') ? linker->LookupPrimitiveClass(class_name[0]) : linker->LookupClass(self, class_name, nullptr); - if (klass == nullptr) { + if (klass == nullptr || !klass->IsResolved()) { return; } - // LOG(INFO) << "VMRuntime.preloadDexCaches resolved klass=" << class_name; dex_cache->SetResolvedType(type_idx, klass); - // Skip uninitialized classes because filled static storage entry implies it is initialized. - if (!klass->IsInitialized()) { - // LOG(INFO) << "VMRuntime.preloadDexCaches uninitialized klass=" << class_name; - return; - } - // LOG(INFO) << "VMRuntime.preloadDexCaches static storage klass=" << class_name; } // Based on ClassLinker::ResolveField. diff --git a/test/180-native-default-method/build b/test/180-native-default-method/build new file mode 100644 index 0000000000..3963fd30b4 --- /dev/null +++ b/test/180-native-default-method/build @@ -0,0 +1,30 @@ +#!/bin/bash +# +# Copyright 2020 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. + +# make us exit on a failure +set -e + +./default-build "$@" + +if [[ $@ != *"--jvm"* ]]; then + # Change the generated dex file to have a v35 magic number if it is version 39 + if test -f classes.dex && head -c 7 classes.dex | grep -q 039; then + # place ascii value '035' into the classes.dex file starting at byte 4. + printf '035' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3 + rm -f $TEST_NAME.jar + zip $TEST_NAME.jar classes.dex + fi +fi diff --git a/test/180-native-default-method/expected.txt b/test/180-native-default-method/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/180-native-default-method/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/180-native-default-method/info.txt b/test/180-native-default-method/info.txt new file mode 100644 index 0000000000..0cba4ebd2e --- /dev/null +++ b/test/180-native-default-method/info.txt @@ -0,0 +1,3 @@ +Regression test for DCHECK() failure for copying a default native method from +an interface to a class implementing that interface. The default native method +should result in ClassFormatError before we reach that DCHECK(). diff --git a/test/180-native-default-method/jasmin/TestClass.j b/test/180-native-default-method/jasmin/TestClass.j new file mode 100644 index 0000000000..fddd99b820 --- /dev/null +++ b/test/180-native-default-method/jasmin/TestClass.j @@ -0,0 +1,25 @@ +; Copyright (C) 2020 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 TestClass +.super java/lang/Object +.implements TestInterface + +.method public <init>()V + .limit stack 1 + .limit locals 1 + aload_0 + invokespecial java/lang/Object/<init>()V + return +.end method diff --git a/test/180-native-default-method/jasmin/TestInterface.j b/test/180-native-default-method/jasmin/TestInterface.j new file mode 100644 index 0000000000..080474e566 --- /dev/null +++ b/test/180-native-default-method/jasmin/TestInterface.j @@ -0,0 +1,19 @@ +; Copyright (C) 2020 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. + +.interface public TestInterface +.super java/lang/Object + +.method public native foo()V +.end method diff --git a/test/180-native-default-method/src/Main.java b/test/180-native-default-method/src/Main.java new file mode 100644 index 0000000000..4b2704b9ec --- /dev/null +++ b/test/180-native-default-method/src/Main.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 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 Main { + public static void main(String args[]) { + try { + // Regression test for default native methods that should cause ClassFormatException + // if they pass the dex file verification, i.e. for old dex file versions. + // We previously did not handle this case properly and failed a DCHECK() for + // a non-interface class creating a copied method that was native. b/157170505 + Class.forName("TestClass"); + throw new Error("UNREACHABLE"); + } catch (ClassFormatError expected) { + System.out.println("passed"); + } catch (Throwable unexpected) { + unexpected.printStackTrace(); + } + } +} |