diff options
| -rw-r--r-- | build/Android.bp | 11 | ||||
| -rw-r--r-- | libnativebridge/include/nativebridge/native_bridge.h | 19 | ||||
| -rw-r--r-- | libnativebridge/native_bridge.cc | 14 | ||||
| -rw-r--r-- | libnativebridge/tests/Android.bp | 19 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp | 31 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h | 27 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp | 39 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridgeTest.h | 1 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridgeTestCase8.cpp | 147 | ||||
| -rw-r--r-- | oatdump/oatdump.cc | 10 | ||||
| -rw-r--r-- | runtime/art_method.h | 3 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 294 | ||||
| -rw-r--r-- | runtime/imtable-inl.h | 51 | ||||
| -rw-r--r-- | runtime/imtable.h | 9 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 8 | ||||
| -rw-r--r-- | tools/fuzzer/class-verifier-corpus/b391844326.dex | bin | 0 -> 1240 bytes |
16 files changed, 492 insertions, 191 deletions
diff --git a/build/Android.bp b/build/Android.bp index e8a0b5424b..17f6891c1b 100644 --- a/build/Android.bp +++ b/build/Android.bp @@ -177,13 +177,6 @@ art_global_defaults { // `--exclude-libs` flag is not supported on windows/darwin. ldflags: ["-Wl,--exclude-libs=libziparchive.a"], }, - linux_bionic: { - strip: { - // Do not strip art libs when building for linux-bionic. - // Otherwise we can't get any symbols out of crashes. - none: true, - }, - }, darwin: { enabled: false, }, @@ -200,6 +193,10 @@ art_global_defaults { // clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress. "-fno-omit-frame-pointer", ], + // Keep the symbols for host to symbolize crash stack traces. + strip: { + none: true, + }, }, // The build assumes that all our x86/x86_64 hosts (such as buildbots and developer // desktops) support at least sse4.2/popcount. This firstly implies that the ART diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h index 491521642c..db3d1e9509 100644 --- a/libnativebridge/include/nativebridge/native_bridge.h +++ b/libnativebridge/include/nativebridge/native_bridge.h @@ -100,6 +100,8 @@ void* NativeBridgeGetTrampolineForFunctionPointer(const void* method, uint32_t len, enum JNICallType jni_call_type); +bool NativeBridgeIsNativeBridgeFunctionPointer(const void* method); + // True if native library paths are valid and is for an ABI that is supported by native bridge. // The *libpath* must point to a library. // @@ -403,9 +405,10 @@ struct NativeBridgeCallbacks { // Get a native bridge trampoline for specified native method implementation pointer. // // Parameters: - // method [IN] pointer to method implementation (ususally registered via call to + // method [IN] pointer to method implementation (usually registered via call to // RegisterNatives). - // shorty [IN] short descriptor of native method len [IN] length of shorty + // shorty [IN] short descriptor of native method + // len [IN] length of shorty // jni_call_type [IN] the type of JNI call // Returns: // address of trampoline if successful, otherwise NULL @@ -413,6 +416,18 @@ struct NativeBridgeCallbacks { const char* shorty, uint32_t len, enum JNICallType jni_call_type); + + // Check if the method pointer belongs to native_bridge address space. + // + // Parameters: + // method [IN] pointer to a method implementation. + // + // Returns: + // true if the method is in native bridge implementation executable address + // space or in other words needs a trampoline to be able to run with native bridge. + // + // Introduced in: version 8 + bool (*isNativeBridgeFunctionPointer)(const void* method); }; // Runtime interfaces to native bridge. diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc index 4461f79919..af85d4ee35 100644 --- a/libnativebridge/native_bridge.cc +++ b/libnativebridge/native_bridge.cc @@ -133,6 +133,8 @@ enum NativeBridgeImplementationVersion { PRE_ZYGOTE_FORK_VERSION = 6, // The version with critical_native support CRITICAL_NATIVE_SUPPORT_VERSION = 7, + // The version with native bridge detection fallback for function pointers + IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION = 8, }; // Whether we had an error at some point. @@ -732,6 +734,18 @@ void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_na return nullptr; } +bool NativeBridgeIsNativeBridgeFunctionPointer(const void* method) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION)) { + return callbacks->isNativeBridgeFunctionPointer(method); + } else { + ALOGW("not compatible with version %d, unable to call isNativeBridgeFunctionPointer", + IDENTIFY_NATIVELY_BRIDGED_FUNCTION_POINTERS_VERSION); + } + } + return false; +} + } // extern "C" } // namespace android diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp index 93ef6feded..add18d12e3 100644 --- a/libnativebridge/tests/Android.bp +++ b/libnativebridge/tests/Android.bp @@ -85,6 +85,15 @@ cc_test_library { ], } +cc_test_library { + name: "libnativebridge8-test-case", + srcs: ["NativeBridgeTestCase8.cpp"], + defaults: ["libnativebridge-test-case-defaults"], + shared_libs: [ + "libnativebridge8IdentifyTrampolines", + ], +} + // A helper library to produce test-case side effect of PreZygoteForkNativeBridge. cc_test_library { name: "libnativebridge6prezygotefork", @@ -98,6 +107,12 @@ cc_test_library { defaults: ["libnativebridge-test-case-defaults"], } +cc_test_library { + name: "libnativebridge8IdentifyTrampolines", + srcs: ["NativeBridge8IdentifyTrampolines_lib.cpp"], + defaults: ["libnativebridge-test-case-defaults"], +} + cc_test { name: "libnativebridge-tests", defaults: [ @@ -130,6 +145,7 @@ cc_test { "NativeBridge3LoadLibraryExt_test.cpp", "NativeBridge6PreZygoteFork_test.cpp", "NativeBridge7CriticalNative_test.cpp", + "NativeBridge8IdentifyTrampolines_test.cpp", ], static_libs: [ @@ -140,10 +156,12 @@ cc_test { "liblog", "libnativebridge6prezygotefork", "libnativebridge7criticalnative", + "libnativebridge8IdentifyTrampolines", ], data_libs: [ "libnativebridge6prezygotefork", "libnativebridge7criticalnative", + "libnativebridge8IdentifyTrampolines", // These are dlopen'd by libnativebridge, not libnativebridge-tests, but // the former is statically linked into the latter, so the linker will @@ -153,6 +171,7 @@ cc_test { "libnativebridge3-test-case", "libnativebridge6-test-case", "libnativebridge7-test-case", + "libnativebridge8-test-case", ], target: { diff --git a/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp new file mode 100644 index 0000000000..0c4ccf03b5 --- /dev/null +++ b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2025 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 "NativeBridge8IdentifyTrampolines_lib.h" + +namespace android { + +static const void* g_is_native_bridge_function_pointer_called_for = nullptr; + +void SetIsNativeBridgeFunctionPointerCalledFor(const void* ptr) { + g_is_native_bridge_function_pointer_called_for = ptr; +} + +bool IsNativeBridgeFunctionPointerCalledFor(const void* ptr) { + return g_is_native_bridge_function_pointer_called_for == ptr; +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h new file mode 100644 index 0000000000..7121e397a4 --- /dev/null +++ b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_lib.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2025 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. + */ + +#ifndef ART_LIBNATIVEBRIDGE_TESTS_NATIVEBRIDGE8IDENTIFYTRAMPOLINES_LIB_H_ +#define ART_LIBNATIVEBRIDGE_TESTS_NATIVEBRIDGE8IDENTIFYTRAMPOLINES_LIB_H_ + +namespace android { + +void SetIsNativeBridgeFunctionPointerCalledFor(const void* ptr); +bool IsNativeBridgeFunctionPointerCalledFor(const void* ptr); + +} // namespace android + +#endif // ART_LIBNATIVEBRIDGE_TESTS_NATIVEBRIDGE8IDENTIFYTRAMPOLINES_LIB_H_ diff --git a/libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp new file mode 100644 index 0000000000..362df54fa1 --- /dev/null +++ b/libnativebridge/tests/NativeBridge8IdentifyTrampolines_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 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 "NativeBridge8IdentifyTrampolines_lib.h" +#include "NativeBridgeTest.h" + +namespace android { + +TEST_F(NativeBridgeTest, V8_IdentifyTrampolines) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary8, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(AppDataDir(), "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(NativeBridgeGetVersion(), 8U); + + const void* ptr = reinterpret_cast<void*>(NativeBridgeGetVersion); + + UNUSED(NativeBridgeIsNativeBridgeFunctionPointer(ptr)); + ASSERT_TRUE(IsNativeBridgeFunctionPointerCalledFor(ptr)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h index 509973d756..88ea0e33c8 100644 --- a/libnativebridge/tests/NativeBridgeTest.h +++ b/libnativebridge/tests/NativeBridgeTest.h @@ -30,6 +30,7 @@ constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-test-case.so"; constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-test-case.so"; constexpr const char* kNativeBridgeLibrary6 = "libnativebridge6-test-case.so"; constexpr const char* kNativeBridgeLibrary7 = "libnativebridge7-test-case.so"; +constexpr const char* kNativeBridgeLibrary8 = "libnativebridge8-test-case.so"; namespace android { diff --git a/libnativebridge/tests/NativeBridgeTestCase8.cpp b/libnativebridge/tests/NativeBridgeTestCase8.cpp new file mode 100644 index 0000000000..c6395f526b --- /dev/null +++ b/libnativebridge/tests/NativeBridgeTestCase8.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2025 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. + */ + +// An implementation of the native-bridge interface for testing. + +#include "NativeBridge8IdentifyTrampolines_lib.h" +#include "nativebridge/native_bridge.h" + +// NativeBridgeCallbacks implementations +extern "C" bool native_bridge8_initialize( + const android::NativeBridgeRuntimeCallbacks* /* art_cbs */, + const char* /* app_code_cache_dir */, + const char* /* isa */) { + return true; +} + +extern "C" void* native_bridge8_loadLibrary(const char* /* libpath */, int /* flag */) { + return nullptr; +} + +extern "C" void* native_bridge8_getTrampoline(void* /* handle */, + const char* /* name */, + const char* /* shorty */, + uint32_t /* len */) { + return nullptr; +} + +extern "C" void* native_bridge8_getTrampoline2(void* /* handle */, + const char* /* name */, + const char* /* shorty */, + uint32_t /* len */, + android::JNICallType /* jni_call_type */) { + return nullptr; +} + +extern "C" void* native_bridge8_getTrampolineForFunctionPointer( + const void* /* method */, + const char* /* shorty */, + uint32_t /* len */, + android::JNICallType /* jni_call_type */) { + return nullptr; +} + +extern "C" bool native_bridge8_isSupported(const char* /* libpath */) { return false; } + +extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge8_getAppEnv( + const char* /* abi */) { + return nullptr; +} + +extern "C" bool native_bridge8_isCompatibleWith(uint32_t version) { + // For testing, allow 1-8, but disallow 9+. + return version <= 8; +} + +extern "C" android::NativeBridgeSignalHandlerFn native_bridge8_getSignalHandler(int /* signal */) { + return nullptr; +} + +extern "C" int native_bridge8_unloadLibrary(void* /* handle */) { return 0; } + +extern "C" const char* native_bridge8_getError() { return nullptr; } + +extern "C" bool native_bridge8_isPathSupported(const char* /* path */) { return true; } + +extern "C" android::native_bridge_namespace_t* native_bridge8_createNamespace( + const char* /* name */, + const char* /* ld_library_path */, + const char* /* default_library_path */, + uint64_t /* type */, + const char* /* permitted_when_isolated_path */, + android::native_bridge_namespace_t* /* parent_ns */) { + return nullptr; +} + +extern "C" bool native_bridge8_linkNamespaces(android::native_bridge_namespace_t* /* from */, + android::native_bridge_namespace_t* /* to */, + const char* /* shared_libs_soname */) { + return true; +} + +extern "C" void* native_bridge8_loadLibraryExt(const char* /* libpath */, + int /* flag */, + android::native_bridge_namespace_t* /* ns */) { + return nullptr; +} + +extern "C" android::native_bridge_namespace_t* native_bridge8_getVendorNamespace() { + return nullptr; +} + +extern "C" android::native_bridge_namespace_t* native_bridge8_getExportedNamespace( + const char* /* name */) { + return nullptr; +} + +extern "C" bool native_bridge8_isNativeBridgeFunctionPointer(const void* ptr) { + android::SetIsNativeBridgeFunctionPointerCalledFor(ptr); + return true; +} + +extern "C" void native_bridge8_preZygoteFork() {} + +android::NativeBridgeCallbacks NativeBridgeItf{ + // v1 + .version = 8, + .initialize = &native_bridge8_initialize, + .loadLibrary = &native_bridge8_loadLibrary, + .getTrampoline = &native_bridge8_getTrampoline, + .isSupported = &native_bridge8_isSupported, + .getAppEnv = &native_bridge8_getAppEnv, + // v2 + .isCompatibleWith = &native_bridge8_isCompatibleWith, + .getSignalHandler = &native_bridge8_getSignalHandler, + // v3 + .unloadLibrary = &native_bridge8_unloadLibrary, + .getError = &native_bridge8_getError, + .isPathSupported = &native_bridge8_isPathSupported, + .unused_initAnonymousNamespace = nullptr, + .createNamespace = &native_bridge8_createNamespace, + .linkNamespaces = &native_bridge8_linkNamespaces, + .loadLibraryExt = &native_bridge8_loadLibraryExt, + // v4 + &native_bridge8_getVendorNamespace, + // v5 + &native_bridge8_getExportedNamespace, + // v6 + &native_bridge8_preZygoteFork, + // v7 + &native_bridge8_getTrampoline2, + &native_bridge8_getTrampolineForFunctionPointer, + // v8 + &native_bridge8_isNativeBridgeFunctionPointer, +}; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index ab22e36c17..b6f681a184 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -3006,10 +3006,18 @@ class IMTDumper { for (ArtMethod& iface_method : iface->GetVirtualMethods(pointer_size)) { uint32_t class_hash, name_hash, signature_hash; - ImTable::GetImtHashComponents(&iface_method, &class_hash, &name_hash, &signature_hash); + ImTable::GetImtHashComponents(*iface_method.GetDexFile(), + iface_method.GetDexMethodIndex(), + &class_hash, + &name_hash, + &signature_hash); uint32_t imt_slot = ImTable::GetImtIndex(&iface_method); + // Note: For default methods we use the dex method index for calculating the slot. + // For abstract methods the compile-time constant `kImTableHashUseName` determines + // whether we use the component hashes (current behavior) or the dex method index. std::cerr << " " << iface_method.PrettyMethod(true) << " slot=" << imt_slot + << " dex_method_index=" << iface_method.GetDexMethodIndex() << std::hex << " class_hash=0x" << class_hash << " name_hash=0x" << name_hash diff --git a/runtime/art_method.h b/runtime/art_method.h index ee11328385..186ff7e45e 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -1126,8 +1126,9 @@ class EXPORT ArtMethod final { // Non-abstract methods: The hotness we measure for this method. Not atomic, // as we allow missing increments: if the method is hot, we will see it eventually. uint16_t hotness_count_; - // Abstract methods: IMT index. + // Abstract interface methods: IMT index. uint16_t imt_index_; + // Abstract class (non-interface) methods: Unused (zero-initialized). }; // Fake padding field gets inserted here. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 73c90f58ac..57eb72dea5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3827,44 +3827,6 @@ class ClassLinker::OatClassCodeIterator { const uint32_t num_methods_; }; -inline void ClassLinker::LinkCode(ArtMethod* method, - uint32_t class_def_method_index, - /*inout*/ OatClassCodeIterator* occi) { - ScopedAssertNoThreadSuspension sants(__FUNCTION__); - Runtime* const runtime = Runtime::Current(); - if (runtime->IsAotCompiler()) { - // The following code only applies to a non-compiler runtime. - return; - } - - // Method shouldn't have already been linked. - DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), nullptr); - DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx. - - if (!method->IsInvokable()) { - EnsureThrowsInvocationError(this, method); - occi->SkipAbstract(class_def_method_index); - return; - } - - const void* quick_code = occi->GetAndAdvance(class_def_method_index); - if (method->IsNative() && quick_code == nullptr) { - const void* boot_jni_stub = FindBootJniStub(method); - if (boot_jni_stub != nullptr) { - // Use boot JNI stub if found. - quick_code = boot_jni_stub; - } - } - runtime->GetInstrumentation()->InitializeMethodsCode(method, quick_code); - - if (method->IsNative()) { - // Set up the dlsym lookup stub. Do not go through `UnregisterNative()` - // as the extra processing for @CriticalNative is not needed yet. - method->SetEntryPointFromJni( - method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub()); - } -} - void ClassLinker::SetupClass(const DexFile& dex_file, const dex::ClassDef& dex_class_def, Handle<mirror::Class> klass, @@ -3968,6 +3930,153 @@ class ClassLinker::MethodAnnotationsIterator { const dex::MethodAnnotationsItem* const end_; }; +void ClassLinker::LoadField(const ClassAccessor::Field& field, + Handle<mirror::Class> klass, + ArtField* dst) { + const uint32_t field_idx = field.GetIndex(); + dst->SetDexFieldIndex(field_idx); + dst->SetDeclaringClass(klass.Get()); + + // Get access flags from the DexFile and set hiddenapi runtime access flags. + dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field)); +} + +void ClassLinker::LoadMethod(const DexFile& dex_file, + const ClassAccessor::Method& method, + ObjPtr<mirror::Class> klass, + /*inout*/ MethodAnnotationsIterator* mai, + /*out*/ ArtMethod* dst) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + + const uint32_t dex_method_idx = method.GetIndex(); + const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); + uint32_t name_utf16_length; + const char* method_name = dex_file.GetStringDataAndUtf16Length(method_id.name_idx_, + &name_utf16_length); + std::string_view shorty = dex_file.GetShortyView(dex_file.GetProtoId(method_id.proto_idx_)); + + dst->SetDexMethodIndex(dex_method_idx); + dst->SetDeclaringClass(klass); + + // Get access flags from the DexFile and set hiddenapi runtime access flags. + uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method); + + auto has_ascii_name = [method_name, name_utf16_length](const char* ascii_name, + size_t length) ALWAYS_INLINE { + DCHECK_EQ(strlen(ascii_name), length); + return length == name_utf16_length && + method_name[length] == 0 && // Is `method_name` an ASCII string? + memcmp(ascii_name, method_name, length) == 0; + }; + if (UNLIKELY(has_ascii_name("finalize", sizeof("finalize") - 1u))) { + // Set finalizable flag on declaring class if the method has the right signature. + // When initializing without a boot image, `Object` and `Enum` shall have the finalizable + // flag cleared immediately after loading these classes, see `InitWithoutImage()`. + if (shorty == "V") { + klass->SetFinalizable(); + } + } else if (method_name[0] == '<') { + // Fix broken access flags for initializers. Bug 11157540. + // `DexFileVerifier` rejects method names starting with '<' other than constructors. + DCHECK(has_ascii_name("<init>", sizeof("<init>") - 1u) || + has_ascii_name("<clinit>", sizeof("<clinit>") - 1u)) << method_name; + if (UNLIKELY((access_flags & kAccConstructor) == 0)) { + LOG(WARNING) << method_name << " didn't have expected constructor access flag in class " + << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation(); + access_flags |= kAccConstructor; + } + } + + access_flags |= GetNterpFastPathFlags(shorty, access_flags, kRuntimeQuickCodeISA); + + if (UNLIKELY((access_flags & kAccNative) != 0u)) { + // Check if the native method is annotated with @FastNative or @CriticalNative. + const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx); + if (method_annotations != nullptr) { + access_flags |= + annotations::GetNativeMethodAnnotationAccessFlags(dex_file, *method_annotations); + } + dst->SetAccessFlags(access_flags); + DCHECK(!dst->IsAbstract()); + DCHECK(!dst->HasCodeItem()); + DCHECK_EQ(method.GetCodeItemOffset(), 0u); + dst->SetDataPtrSize(nullptr, image_pointer_size_); // JNI stub/trampoline not linked yet. + } else if ((access_flags & kAccAbstract) != 0u) { + dst->SetAccessFlags(access_flags); + // Must be done after SetAccessFlags since IsAbstract depends on it. + DCHECK(dst->IsAbstract()); + if (klass->IsInterface()) { + dst->CalculateAndSetImtIndex(); + } + DCHECK(!dst->HasCodeItem()); + DCHECK_EQ(method.GetCodeItemOffset(), 0u); + dst->SetDataPtrSize(nullptr, image_pointer_size_); // Single implementation not set yet. + } else { + const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx); + if (method_annotations != nullptr && + annotations::MethodIsNeverCompile(dex_file, *method_annotations)) { + access_flags |= kAccCompileDontBother; + } + dst->SetAccessFlags(access_flags); + DCHECK(!dst->IsAbstract()); + DCHECK(dst->HasCodeItem()); + uint32_t code_item_offset = method.GetCodeItemOffset(); + DCHECK_NE(code_item_offset, 0u); + if (Runtime::Current()->IsAotCompiler()) { + dst->SetDataPtrSize(reinterpret_cast32<void*>(code_item_offset), image_pointer_size_); + } else { + dst->SetCodeItem(dex_file.GetCodeItem(code_item_offset), dex_file.IsCompactDexFile()); + } + } + + if ((access_flags & kAccAbstract) == 0u && + Runtime::Current()->IsZygote() && + !Runtime::Current()->GetJITOptions()->GetProfileSaverOptions().GetProfileBootClassPath()) { + DCHECK(!ArtMethod::IsAbstract(access_flags)); + DCHECK(!ArtMethod::IsIntrinsic(access_flags)); + dst->SetMemorySharedMethod(); + dst->SetHotCounter(); + } +} + +inline void ClassLinker::LinkCode(ArtMethod* method, + uint32_t class_def_method_index, + /*inout*/ OatClassCodeIterator* occi) { + ScopedAssertNoThreadSuspension sants(__FUNCTION__); + Runtime* const runtime = Runtime::Current(); + if (runtime->IsAotCompiler()) { + // The following code only applies to a non-compiler runtime. + return; + } + + // Method shouldn't have already been linked. + DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), nullptr); + DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx. + + if (!method->IsInvokable()) { + EnsureThrowsInvocationError(this, method); + occi->SkipAbstract(class_def_method_index); + return; + } + + const void* quick_code = occi->GetAndAdvance(class_def_method_index); + if (method->IsNative() && quick_code == nullptr) { + const void* boot_jni_stub = FindBootJniStub(method); + if (boot_jni_stub != nullptr) { + // Use boot JNI stub if found. + quick_code = boot_jni_stub; + } + } + runtime->GetInstrumentation()->InitializeMethodsCode(method, quick_code); + + if (method->IsNative()) { + // Set up the dlsym lookup stub. Do not go through `UnregisterNative()` + // as the extra processing for @CriticalNative is not needed yet. + method->SetEntryPointFromJni( + method->IsCriticalNative() ? GetJniDlsymLookupCriticalStub() : GetJniDlsymLookupStub()); + } +} + void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, const dex::ClassDef& dex_class_def, @@ -4090,115 +4199,6 @@ void ClassLinker::LoadClass(Thread* self, self->AllowThreadSuspension(); } -void ClassLinker::LoadField(const ClassAccessor::Field& field, - Handle<mirror::Class> klass, - ArtField* dst) { - const uint32_t field_idx = field.GetIndex(); - dst->SetDexFieldIndex(field_idx); - dst->SetDeclaringClass(klass.Get()); - - // Get access flags from the DexFile and set hiddenapi runtime access flags. - dst->SetAccessFlags(field.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(field)); -} - -void ClassLinker::LoadMethod(const DexFile& dex_file, - const ClassAccessor::Method& method, - ObjPtr<mirror::Class> klass, - /*inout*/ MethodAnnotationsIterator* mai, - /*out*/ ArtMethod* dst) { - ScopedAssertNoThreadSuspension sants(__FUNCTION__); - - const uint32_t dex_method_idx = method.GetIndex(); - const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); - uint32_t name_utf16_length; - const char* method_name = dex_file.GetStringDataAndUtf16Length(method_id.name_idx_, - &name_utf16_length); - std::string_view shorty = dex_file.GetShortyView(dex_file.GetProtoId(method_id.proto_idx_)); - - dst->SetDexMethodIndex(dex_method_idx); - dst->SetDeclaringClass(klass); - - // Get access flags from the DexFile and set hiddenapi runtime access flags. - uint32_t access_flags = method.GetAccessFlags() | hiddenapi::CreateRuntimeFlags(method); - - auto has_ascii_name = [method_name, name_utf16_length](const char* ascii_name, - size_t length) ALWAYS_INLINE { - DCHECK_EQ(strlen(ascii_name), length); - return length == name_utf16_length && - method_name[length] == 0 && // Is `method_name` an ASCII string? - memcmp(ascii_name, method_name, length) == 0; - }; - if (UNLIKELY(has_ascii_name("finalize", sizeof("finalize") - 1u))) { - // Set finalizable flag on declaring class if the method has the right signature. - // When initializing without a boot image, `Object` and `Enum` shall have the finalizable - // flag cleared immediately after loading these classes, see `InitWithoutImage()`. - if (shorty == "V") { - klass->SetFinalizable(); - } - } else if (method_name[0] == '<') { - // Fix broken access flags for initializers. Bug 11157540. - // `DexFileVerifier` rejects method names starting with '<' other than constructors. - DCHECK(has_ascii_name("<init>", sizeof("<init>") - 1u) || - has_ascii_name("<clinit>", sizeof("<clinit>") - 1u)) << method_name; - if (UNLIKELY((access_flags & kAccConstructor) == 0)) { - LOG(WARNING) << method_name << " didn't have expected constructor access flag in class " - << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation(); - access_flags |= kAccConstructor; - } - } - - access_flags |= GetNterpFastPathFlags(shorty, access_flags, kRuntimeQuickCodeISA); - - if (UNLIKELY((access_flags & kAccNative) != 0u)) { - // Check if the native method is annotated with @FastNative or @CriticalNative. - const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx); - if (method_annotations != nullptr) { - access_flags |= - annotations::GetNativeMethodAnnotationAccessFlags(dex_file, *method_annotations); - } - dst->SetAccessFlags(access_flags); - DCHECK(!dst->IsAbstract()); - DCHECK(!dst->HasCodeItem()); - DCHECK_EQ(method.GetCodeItemOffset(), 0u); - dst->SetDataPtrSize(nullptr, image_pointer_size_); // JNI stub/trampoline not linked yet. - } else if ((access_flags & kAccAbstract) != 0u) { - dst->SetAccessFlags(access_flags); - // Must be done after SetAccessFlags since IsAbstract depends on it. - DCHECK(dst->IsAbstract()); - if (klass->IsInterface()) { - dst->CalculateAndSetImtIndex(); - } - DCHECK(!dst->HasCodeItem()); - DCHECK_EQ(method.GetCodeItemOffset(), 0u); - dst->SetDataPtrSize(nullptr, image_pointer_size_); // Single implementation not set yet. - } else { - const dex::MethodAnnotationsItem* method_annotations = mai->AdvanceTo(dex_method_idx); - if (method_annotations != nullptr && - annotations::MethodIsNeverCompile(dex_file, *method_annotations)) { - access_flags |= kAccCompileDontBother; - } - dst->SetAccessFlags(access_flags); - DCHECK(!dst->IsAbstract()); - DCHECK(dst->HasCodeItem()); - uint32_t code_item_offset = method.GetCodeItemOffset(); - DCHECK_NE(code_item_offset, 0u); - if (Runtime::Current()->IsAotCompiler()) { - dst->SetDataPtrSize(reinterpret_cast32<void*>(code_item_offset), image_pointer_size_); - } else { - dst->SetCodeItem(dex_file.GetCodeItem(code_item_offset), dex_file.IsCompactDexFile()); - } - } - - if ((access_flags & kAccAbstract) == 0u && - Runtime::Current()->IsZygote() && - !Runtime::Current()->GetJITOptions()->GetProfileSaverOptions().GetProfileBootClassPath()) { - DCHECK(!ArtMethod::IsAbstract(access_flags)); - DCHECK(!ArtMethod::IsIntrinsic(access_flags)); - dst->SetMemorySharedMethod(); - dst->SetHotCounter(); - } -} - void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile* dex_file) { ObjPtr<mirror::DexCache> dex_cache = AllocAndInitializeDexCache(self, *dex_file, /* class_loader= */ nullptr); diff --git a/runtime/imtable-inl.h b/runtime/imtable-inl.h index 9be56cb8f8..8af1d89f60 100644 --- a/runtime/imtable-inl.h +++ b/runtime/imtable-inl.h @@ -33,65 +33,52 @@ static constexpr uint32_t kImTableHashCoefficientClass = 427; static constexpr uint32_t kImTableHashCoefficientName = 16; static constexpr uint32_t kImTableHashCoefficientSignature = 14; -inline void ImTable::GetImtHashComponents(ArtMethod* method, +inline void ImTable::GetImtHashComponents(const DexFile& dex_file, + uint32_t dex_method_index, uint32_t* class_hash, uint32_t* name_hash, uint32_t* signature_hash) { if (kImTableHashUseName) { - if (method->IsProxyMethod()) { - *class_hash = 0; - *name_hash = 0; - *signature_hash = 0; - return; - } - - const DexFile* dex_file = method->GetDexFile(); - const dex::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex()); + const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_index); // Class descriptor for the class component. - *class_hash = ComputeModifiedUtf8Hash(dex_file->GetMethodDeclaringClassDescriptor(method_id)); + *class_hash = ComputeModifiedUtf8Hash(dex_file.GetMethodDeclaringClassDescriptor(method_id)); // Method name for the method component. - *name_hash = ComputeModifiedUtf8Hash(dex_file->GetMethodName(method_id)); + *name_hash = ComputeModifiedUtf8Hash(dex_file.GetMethodName(method_id)); - const dex::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); + const dex::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id); // Read the proto for the signature component. uint32_t tmp = ComputeModifiedUtf8Hash( - dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_))); + dex_file.GetTypeDescriptor(dex_file.GetTypeId(proto_id.return_type_idx_))); // Mix in the argument types. // Note: we could consider just using the shorty. This would be faster, at the price of // potential collisions. - const dex::TypeList* param_types = dex_file->GetProtoParameters(proto_id); + const dex::TypeList* param_types = dex_file.GetProtoParameters(proto_id); if (param_types != nullptr) { for (size_t i = 0; i != param_types->Size(); ++i) { const dex::TypeItem& type = param_types->GetTypeItem(i); tmp = 31 * tmp + ComputeModifiedUtf8Hash( - dex_file->GetTypeDescriptor(dex_file->GetTypeId(type.type_idx_))); + dex_file.GetTypeDescriptor(dex_file.GetTypeId(type.type_idx_))); } } *signature_hash = tmp; return; } else { - *class_hash = method->GetDexMethodIndex(); + *class_hash = dex_method_index; *name_hash = 0; *signature_hash = 0; return; } } -inline uint32_t ImTable::GetImtIndex(ArtMethod* method) { - DCHECK(!method->IsCopied()); - if (!method->IsAbstract()) { - // For default methods, where we cannot store the imt_index, we use the - // method_index instead. We mask it with the closest power of two to - // simplify the interpreter. - return method->GetMethodIndex() & (ImTable::kSizeTruncToPowerOfTwo - 1); - } +inline uint32_t ImTable::GetImtIndexForAbstractMethod(const DexFile& dex_file, + uint32_t dex_method_index) { uint32_t class_hash, name_hash, signature_hash; - GetImtHashComponents(method, &class_hash, &name_hash, &signature_hash); + GetImtHashComponents(dex_file, dex_method_index, &class_hash, &name_hash, &signature_hash); uint32_t mixed_hash; if (!kImTableHashUseCoefficients) { @@ -105,6 +92,18 @@ inline uint32_t ImTable::GetImtIndex(ArtMethod* method) { return mixed_hash % ImTable::kSize; } +inline uint32_t ImTable::GetImtIndex(ArtMethod* method) { + DCHECK(!method->IsCopied()); + DCHECK(!method->IsProxyMethod()); + if (!method->IsAbstract()) { + // For default methods, where we cannot store the imt_index, we use the + // method_index instead. We mask it with the closest power of two to + // simplify the interpreter. + return method->GetMethodIndex() & (ImTable::kSizeTruncToPowerOfTwo - 1); + } + return GetImtIndexForAbstractMethod(*method->GetDexFile(), method->GetDexMethodIndex()); +} + } // namespace art #endif // ART_RUNTIME_IMTABLE_INL_H_ diff --git a/runtime/imtable.h b/runtime/imtable.h index 0d604ca244..f7e9066c78 100644 --- a/runtime/imtable.h +++ b/runtime/imtable.h @@ -81,11 +81,14 @@ class ImTable { } // Converts a method to the base hash components used in GetImtIndex. - ALWAYS_INLINE static inline void GetImtHashComponents(ArtMethod* method, + ALWAYS_INLINE static inline void GetImtHashComponents(const DexFile& dex_file, + uint32_t dex_method_index, uint32_t* class_hash, uint32_t* name_hash, - uint32_t* signature_hash) - REQUIRES_SHARED(Locks::mutator_lock_); + uint32_t* signature_hash); + + ALWAYS_INLINE static inline uint32_t GetImtIndexForAbstractMethod(const DexFile& dex_file, + uint32_t dex_method_index); // The (complete) hashing scheme to map an ArtMethod to a slot in the Interface Method Table // (IMT). diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 05fb9b39b6..1de1e6bce8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1593,6 +1593,10 @@ bool MethodVerifier<kVerifierDebug>::Verify() { return false; } + if (code_item_accessor_.InsnsSizeInCodeUnits() == 0u) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code item has no opcode"; + return false; + } // Allocate and initialize an array to hold instruction data. insn_flags_.reset(allocator_.AllocArray<InstructionFlags>( code_item_accessor_.InsnsSizeInCodeUnits())); @@ -1619,10 +1623,6 @@ bool MethodVerifierImpl::ComputeWidthsAndCountOps() { // We can't assume the instruction is well formed, handle the case where calculating the size // goes past the end of the code item. const uint32_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits(); - if (insns_size == 0u) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code item has no opcode"; - return false; - } const Instruction* inst = &code_item_accessor_.InstructionAt(0u); uint32_t dex_pc = 0u; while (dex_pc != insns_size) { diff --git a/tools/fuzzer/class-verifier-corpus/b391844326.dex b/tools/fuzzer/class-verifier-corpus/b391844326.dex Binary files differnew file mode 100644 index 0000000000..faf536139b --- /dev/null +++ b/tools/fuzzer/class-verifier-corpus/b391844326.dex |