Refactor Core Plaform API checking
Move Core Platform API check logic to hidden_api_jni.{h,cc}.
Use libunwindstack for walking stack as builtin isn't robust without
frame pointers. This is moderately expensive so we currently walk a
maximum of 3 frames.
Checks are now performed by placing ScopedCorePlatformApiCheck
instances at potential points of entry via JNI. Specifically, where
JNI interfaces implement methods for getting field and method ids. The
check is only performed on the outermost instance of the
ScopedCorePlatformApiCheck since only the outer caller is of interest.
State for the checks is stored in the current Thread instance since
the JNI interface signatures are fixed and we need a way to be able to
determine whether a ScopedCorePlatformApiCheck is the outermost
instance and whether the outmost instance was approved.
Test: build eng build (has checkjni), boot, grep logcat nio/Buffer
Test: build user build (regular jni), boot, grep logcat nio/Buffer
Bug: 130336799
Bug: 136276414
Change-Id: If1261046dd57c007bc77b213daaa5f733151123e
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 895e2c6..d064520 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -87,6 +87,7 @@
"gc/task_processor.cc",
"gc/verification.cc",
"hidden_api.cc",
+ "hidden_api_jni.cc",
"hprof/hprof.cc",
"image.cc",
"index_bss_mapping.cc",
diff --git a/runtime/hidden_api_jni.cc b/runtime/hidden_api_jni.cc
new file mode 100644
index 0000000..074c4c3
--- /dev/null
+++ b/runtime/hidden_api_jni.cc
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+
+#include "hidden_api_jni.h"
+#include "hidden_api.h"
+
+#if defined(__linux__)
+
+#include <dlfcn.h>
+#include <link.h>
+
+#include <mutex>
+
+#include "android-base/logging.h"
+
+#include "unwindstack/Regs.h"
+#include "unwindstack/RegsGetLocal.h"
+#include "unwindstack/Memory.h"
+#include "unwindstack/Unwinder.h"
+
+#include "base/bit_utils.h"
+#include "base/casts.h"
+#include "base/file_utils.h"
+#include "base/globals.h"
+#include "base/memory_type_table.h"
+#include "base/string_view_cpp20.h"
+
+namespace art {
+namespace hiddenapi {
+
+namespace {
+
+// The maximum number of frames to back trace through when performing Core Platform API checks of
+// native code.
+static constexpr size_t kMaxFrames = 3;
+
+static std::mutex gUnwindingMutex;
+
+struct UnwindHelper {
+ explicit UnwindHelper(size_t max_depth)
+ : memory_(unwindstack::Memory::CreateProcessMemory(getpid())),
+ jit_(memory_),
+ dex_(memory_),
+ unwinder_(max_depth, &maps_, memory_) {
+ CHECK(maps_.Parse());
+ unwinder_.SetJitDebug(&jit_, unwindstack::Regs::CurrentArch());
+ unwinder_.SetDexFiles(&dex_, unwindstack::Regs::CurrentArch());
+ unwinder_.SetResolveNames(false);
+ unwindstack::Elf::SetCachingEnabled(false);
+ }
+
+ unwindstack::Unwinder* Unwinder() { return &unwinder_; }
+
+ private:
+ unwindstack::LocalMaps maps_;
+ std::shared_ptr<unwindstack::Memory> memory_;
+ unwindstack::JitDebug jit_;
+ unwindstack::DexFiles dex_;
+ unwindstack::Unwinder unwinder_;
+};
+
+static UnwindHelper& GetUnwindHelper() {
+ static UnwindHelper helper(kMaxFrames);
+ return helper;
+}
+
+} // namespace
+
+enum class SharedObjectKind {
+ kRuntime = 0,
+ kApexModule = 1,
+ kOther = 2
+};
+
+std::ostream& operator<<(std::ostream& os, SharedObjectKind kind) {
+ switch (kind) {
+ case SharedObjectKind::kRuntime:
+ os << "Runtime";
+ break;
+ case SharedObjectKind::kApexModule:
+ os << "APEX Module";
+ break;
+ case SharedObjectKind::kOther:
+ os << "Other";
+ break;
+ }
+ return os;
+}
+
+// Class holding Cached ranges of loaded shared objects to facilitate checks of field and method
+// resolutions within the Core Platform API for native callers.
+class CodeRangeCache final {
+ public:
+ static CodeRangeCache& GetSingleton() {
+ static CodeRangeCache Singleton;
+ return Singleton;
+ }
+
+ SharedObjectKind GetSharedObjectKind(void* pc) {
+ uintptr_t address = reinterpret_cast<uintptr_t>(pc);
+ SharedObjectKind kind;
+ if (Find(address, &kind)) {
+ return kind;
+ }
+ return SharedObjectKind::kOther;
+ }
+
+ bool HasCache() const {
+ return memory_type_table_.Size() != 0;
+ }
+
+ void BuildCache() {
+ DCHECK(!HasCache());
+ art::MemoryTypeTable<SharedObjectKind>::Builder builder;
+ builder_ = &builder;
+ libjavacore_loaded_ = false;
+ libnativehelper_loaded_ = false;
+ libopenjdk_loaded_ = false;
+
+ // Iterate over ELF headers populating table_builder with executable ranges.
+ dl_iterate_phdr(VisitElfInfo, this);
+ memory_type_table_ = builder_->Build();
+
+ // Check expected libraries loaded when iterating headers.
+ CHECK(libjavacore_loaded_);
+ CHECK(libnativehelper_loaded_);
+ CHECK(libopenjdk_loaded_);
+ builder_ = nullptr;
+ }
+
+ void DropCache() {
+ memory_type_table_ = {};
+ }
+
+ private:
+ CodeRangeCache() {}
+
+ bool Find(uintptr_t address, SharedObjectKind* kind) const {
+ const art::MemoryTypeRange<SharedObjectKind>* range = memory_type_table_.Lookup(address);
+ if (range == nullptr) {
+ return false;
+ }
+ *kind = range->Type();
+ return true;
+ }
+
+ static int VisitElfInfo(struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, void *data)
+ NO_THREAD_SAFETY_ANALYSIS {
+ auto cache = reinterpret_cast<CodeRangeCache*>(data);
+ art::MemoryTypeTable<SharedObjectKind>::Builder* builder = cache->builder_;
+
+ for (size_t i = 0u; i < info->dlpi_phnum; ++i) {
+ const ElfW(Phdr)& phdr = info->dlpi_phdr[i];
+ if (phdr.p_type != PT_LOAD || ((phdr.p_flags & PF_X) != PF_X)) {
+ continue; // Skip anything other than code pages
+ }
+ uintptr_t start = info->dlpi_addr + phdr.p_vaddr;
+ const uintptr_t limit = art::RoundUp(start + phdr.p_memsz, art::kPageSize);
+ SharedObjectKind kind = GetKind(info->dlpi_name, start, limit);
+ art::MemoryTypeRange<SharedObjectKind> range{start, limit, kind};
+ if (!builder->Add(range)) {
+ LOG(WARNING) << "Overlapping/invalid range found in ELF headers: " << range;
+ }
+ }
+
+ // Update sanity check state.
+ std::string_view dlpi_name{info->dlpi_name};
+ if (!cache->libjavacore_loaded_) {
+ cache->libjavacore_loaded_ = art::EndsWith(dlpi_name, kLibjavacore);
+ }
+ if (!cache->libnativehelper_loaded_) {
+ cache->libnativehelper_loaded_ = art::EndsWith(dlpi_name, kLibnativehelper);
+ }
+ if (!cache->libopenjdk_loaded_) {
+ cache->libopenjdk_loaded_ = art::EndsWith(dlpi_name, kLibopenjdk);
+ }
+
+ return 0;
+ }
+
+ static SharedObjectKind GetKind(const char* so_name, uintptr_t start, uintptr_t limit) {
+ uintptr_t runtime_method = reinterpret_cast<uintptr_t>(CodeRangeCache::GetKind);
+ if (runtime_method >= start && runtime_method < limit) {
+ return SharedObjectKind::kRuntime;
+ }
+ return art::LocationIsOnApex(so_name) ? SharedObjectKind::kApexModule
+ : SharedObjectKind::kOther;
+ }
+
+ art::MemoryTypeTable<SharedObjectKind> memory_type_table_;
+
+ // Table builder, only valid during BuildCache().
+ art::MemoryTypeTable<SharedObjectKind>::Builder* builder_;
+
+ // Sanity checking state.
+ bool libjavacore_loaded_;
+ bool libnativehelper_loaded_;
+ bool libopenjdk_loaded_;
+
+ static constexpr std::string_view kLibjavacore = "libjavacore.so";
+ static constexpr std::string_view kLibnativehelper = "libnativehelper.so";
+ static constexpr std::string_view kLibopenjdk = art::kIsDebugBuild ? "libopenjdkd.so"
+ : "libopenjdk.so";
+
+ DISALLOW_COPY_AND_ASSIGN(CodeRangeCache);
+};
+
+// Cookie for tracking approvals of Core Platform API use. The Thread class has a per thread field
+// that stores these values. This is necessary because we can't change the JNI interfaces and some
+// paths call into each other, ie checked JNI typically calls plain JNI.
+struct CorePlatformApiCookie final {
+ bool approved:1; // Whether the outermost ScopedCorePlatformApiCheck instance is approved.
+ uint32_t depth:31; // Count of nested ScopedCorePlatformApiCheck instances.
+};
+
+ScopedCorePlatformApiCheck::ScopedCorePlatformApiCheck() {
+ Thread* self = Thread::Current();
+ CorePlatformApiCookie cookie =
+ bit_cast<CorePlatformApiCookie, uint32_t>(self->CorePlatformApiCookie());
+ bool is_core_platform_api_approved = false; // Default value for non-device testing.
+ if (!kIsTargetBuild) {
+ // On target device, if policy says enforcement is disabled, then treat all callers as
+ // approved.
+ auto policy = Runtime::Current()->GetCorePlatformApiEnforcementPolicy();
+ if (policy == hiddenapi::EnforcementPolicy::kDisabled) {
+ is_core_platform_api_approved = true;
+ } else if (cookie.depth == 0) {
+ // On target device, only check the caller at depth 0 (the outermost entry into JNI
+ // interface).
+ DCHECK_EQ(cookie.approved, false);
+ void* caller_pc = CaptureCallerPc();
+ if (caller_pc != nullptr) {
+ SharedObjectKind kind = CodeRangeCache::GetSingleton().GetSharedObjectKind(caller_pc);
+ is_core_platform_api_approved = ((kind == SharedObjectKind::kRuntime) ||
+ (kind == SharedObjectKind::kApexModule));
+ }
+ }
+ }
+
+ // Update cookie
+ if (is_core_platform_api_approved) {
+ cookie.approved = true;
+ }
+ cookie.depth += 1;
+ self->SetCorePlatformApiCookie(bit_cast<uint32_t, CorePlatformApiCookie>(cookie));
+}
+
+ScopedCorePlatformApiCheck::~ScopedCorePlatformApiCheck() {
+ Thread* self = Thread::Current();
+ // Update cookie, decrementing depth and clearing approved flag if this is the outermost
+ // instance.
+ CorePlatformApiCookie cookie =
+ bit_cast<CorePlatformApiCookie, uint32_t>(self->CorePlatformApiCookie());
+ DCHECK_NE(cookie.depth, 0u);
+ cookie.depth -= 1u;
+ if (cookie.depth == 0u) {
+ cookie.approved = false;
+ }
+ self->SetCorePlatformApiCookie(bit_cast<uint32_t, CorePlatformApiCookie>(cookie));
+}
+
+bool ScopedCorePlatformApiCheck::IsCurrentCallerApproved(Thread* self) {
+ CorePlatformApiCookie cookie =
+ bit_cast<CorePlatformApiCookie, uint32_t>(self->CorePlatformApiCookie());
+ DCHECK_GT(cookie.depth, 0u);
+ return cookie.approved;
+}
+
+void* ScopedCorePlatformApiCheck::CaptureCallerPc() {
+ std::lock_guard<std::mutex> guard(gUnwindingMutex);
+ unwindstack::Unwinder* unwinder = GetUnwindHelper().Unwinder();
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+ unwinder->SetRegs(regs.get());
+ unwinder->Unwind();
+ for (auto it = unwinder->frames().begin(); it != unwinder->frames().end(); ++it) {
+ // Unwind to frame above the tlsJniStackMarker. The stack markers should be on the first frame
+ // calling JNI methods.
+ if (it->sp > reinterpret_cast<uint64_t>(this)) {
+ return reinterpret_cast<void*>(it->pc);
+ }
+ }
+ return nullptr;
+}
+
+void JniInitializeNativeCallerCheck() {
+ // This method should be called only once and before there are multiple runtime threads.
+ DCHECK(!CodeRangeCache::GetSingleton().HasCache());
+ CodeRangeCache::GetSingleton().BuildCache();
+}
+
+void JniShutdownNativeCallerCheck() {
+ CodeRangeCache::GetSingleton().DropCache();
+}
+
+} // namespace hiddenapi
+} // namespace art
+
+#else // __linux__
+
+namespace art {
+namespace hiddenapi {
+
+ScopedCorePlatformApiCheck::ScopedCorePlatformApiCheck() {}
+
+ScopedCorePlatformApiCheck::~ScopedCorePlatformApiCheck() {}
+
+bool ScopedCorePlatformApiCheck::IsCurrentCallerApproved(Thread* self ATTRIBUTE_UNUSED) {
+ return false;
+}
+
+void JniInitializeNativeCallerCheck() {}
+
+void JniShutdownNativeCallerCheck() {}
+
+} // namespace hiddenapi
+} // namespace art
+
+#endif // __linux__
diff --git a/runtime/hidden_api_jni.h b/runtime/hidden_api_jni.h
new file mode 100644
index 0000000..a084378
--- /dev/null
+++ b/runtime/hidden_api_jni.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_HIDDEN_API_JNI_H_
+#define ART_RUNTIME_HIDDEN_API_JNI_H_
+
+#include "base/macros.h"
+
+namespace art {
+
+class Thread;
+
+namespace hiddenapi {
+
+// Stack markers that should be instantiated in JNI Get{Field,Method}Id methods (and
+// their static equivalents) to allow native caller checks to take place.
+class ScopedCorePlatformApiCheck final {
+ public:
+ ScopedCorePlatformApiCheck();
+ ~ScopedCorePlatformApiCheck();
+
+ // Check whether the caller is automatically approved based on location. Code in the run-time or
+ // in an APEX is considered to be automatically approved.
+ static bool IsCurrentCallerApproved(Thread* self);
+
+ private:
+ // Captures calling PC for frame above the frame allocating the current ScopedCorePlatformApiCheck
+ // instance.
+ void* CaptureCallerPc();
+
+ // Instances should only be stack allocated, copy and assignment not useful.
+ DISALLOW_ALLOCATION();
+ DISALLOW_COPY_AND_ASSIGN(ScopedCorePlatformApiCheck);
+};
+
+void JniInitializeNativeCallerCheck();
+void JniShutdownNativeCallerCheck();
+
+} // namespace hiddenapi
+} // namespace art
+
+#endif // ART_RUNTIME_HIDDEN_API_JNI_H_
diff --git a/runtime/jni/check_jni.cc b/runtime/jni/check_jni.cc
index 4a8453a..3491280 100644
--- a/runtime/jni/check_jni.cc
+++ b/runtime/jni/check_jni.cc
@@ -35,6 +35,7 @@
#include "dex/descriptors_names.h"
#include "dex/dex_file-inl.h"
#include "gc/space/space.h"
+#include "hidden_api_jni.h"
#include "java_vm_ext.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
@@ -2221,18 +2222,22 @@
}
static jmethodID GetMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, false);
}
static jmethodID GetStaticMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return GetMethodIDInternal(__FUNCTION__, env, c, name, sig, true);
}
static jfieldID GetFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, false);
}
static jfieldID GetStaticFieldID(JNIEnv* env, jclass c, const char* name, const char* sig) {
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, true);
}
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index d9a7186..3d38881 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -18,30 +18,25 @@
#include <cstdarg>
#include <memory>
-#include <mutex>
#include <utility>
-#include <link.h>
-
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/allocator.h"
#include "base/atomic.h"
-#include "base/bit_utils.h"
#include "base/enums.h"
#include "base/file_utils.h"
#include "base/logging.h" // For VLOG.
-#include "base/memory_type_table.h"
#include "base/mutex.h"
#include "base/safe_map.h"
#include "base/stl_util.h"
-#include "base/string_view_cpp20.h"
#include "class_linker-inl.h"
#include "class_root.h"
#include "dex/dex_file-inl.h"
#include "dex/utf.h"
#include "fault_handler.h"
#include "hidden_api.h"
+#include "hidden_api_jni.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root.h"
#include "indirect_reference_table-inl.h"
@@ -82,180 +77,6 @@
va_list* args;
};
-static constexpr size_t kMaxReturnAddressDepth = 4;
-
-inline void* GetReturnAddress(size_t depth) {
- DCHECK_LT(depth, kMaxReturnAddressDepth);
- switch (depth) {
- case 0u: return __builtin_return_address(0);
- case 1u: return __builtin_return_address(1);
- case 2u: return __builtin_return_address(2);
- case 3u: return __builtin_return_address(3);
- default:
- return nullptr;
- }
-}
-
-enum class SharedObjectKind {
- kRuntime = 0,
- kApexModule = 1,
- kOther = 2
-};
-
-std::ostream& operator<<(std::ostream& os, SharedObjectKind kind) {
- switch (kind) {
- case SharedObjectKind::kRuntime:
- os << "Runtime";
- break;
- case SharedObjectKind::kApexModule:
- os << "APEX Module";
- break;
- case SharedObjectKind::kOther:
- os << "Other";
- break;
- }
- return os;
-}
-
-// Class holding Cached ranges of loaded shared objects to facilitate checks of field and method
-// resolutions within the Core Platform API for native callers.
-class CodeRangeCache final {
- public:
- static CodeRangeCache& GetSingleton() {
- static CodeRangeCache Singleton;
- return Singleton;
- }
-
- SharedObjectKind GetSharedObjectKind(void* pc) {
- uintptr_t address = reinterpret_cast<uintptr_t>(pc);
- SharedObjectKind kind;
- if (Find(address, &kind)) {
- return kind;
- }
- return SharedObjectKind::kOther;
- }
-
- bool HasCache() const {
- return memory_type_table_.Size() != 0;
- }
-
- void BuildCache() {
- DCHECK(!HasCache());
- art::MemoryTypeTable<SharedObjectKind>::Builder builder;
- builder_ = &builder;
- libjavacore_loaded_ = false;
- libnativehelper_loaded_ = false;
- libopenjdk_loaded_ = false;
-
- // Iterate over ELF headers populating table_builder with executable ranges.
- dl_iterate_phdr(VisitElfInfo, this);
- memory_type_table_ = builder_->Build();
-
- // Check expected libraries loaded when iterating headers.
- CHECK(libjavacore_loaded_);
- CHECK(libnativehelper_loaded_);
- CHECK(libopenjdk_loaded_);
- builder_ = nullptr;
- }
-
- void DropCache() {
- memory_type_table_ = {};
- }
-
- private:
- CodeRangeCache() {}
-
- bool Find(uintptr_t address, SharedObjectKind* kind) const {
- const art::MemoryTypeRange<SharedObjectKind>* range = memory_type_table_.Lookup(address);
- if (range == nullptr) {
- return false;
- }
- *kind = range->Type();
- return true;
- }
-
- static int VisitElfInfo(struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED, void *data)
- NO_THREAD_SAFETY_ANALYSIS {
- auto cache = reinterpret_cast<CodeRangeCache*>(data);
- art::MemoryTypeTable<SharedObjectKind>::Builder* builder = cache->builder_;
-
- for (size_t i = 0u; i < info->dlpi_phnum; ++i) {
- const ElfW(Phdr)& phdr = info->dlpi_phdr[i];
- if (phdr.p_type != PT_LOAD || ((phdr.p_flags & PF_X) != PF_X)) {
- continue; // Skip anything other than code pages
- }
- uintptr_t start = info->dlpi_addr + phdr.p_vaddr;
- const uintptr_t limit = art::RoundUp(start + phdr.p_memsz, art::kPageSize);
- SharedObjectKind kind = GetKind(info->dlpi_name, start, limit);
- art::MemoryTypeRange<SharedObjectKind> range{start, limit, kind};
- if (!builder->Add(range)) {
- LOG(WARNING) << "Overlapping/invalid range found in ELF headers: " << range;
- }
- }
-
- // Update sanity check state.
- std::string_view dlpi_name{info->dlpi_name};
- if (!cache->libjavacore_loaded_) {
- cache->libjavacore_loaded_ = art::EndsWith(dlpi_name, kLibjavacore);
- }
- if (!cache->libnativehelper_loaded_) {
- cache->libnativehelper_loaded_ = art::EndsWith(dlpi_name, kLibnativehelper);
- }
- if (!cache->libopenjdk_loaded_) {
- cache->libopenjdk_loaded_ = art::EndsWith(dlpi_name, kLibopenjdk);
- }
-
- return 0;
- }
-
- static SharedObjectKind GetKind(const char* so_name, uintptr_t start, uintptr_t limit) {
- uintptr_t runtime_method = reinterpret_cast<uintptr_t>(art::GetJniNativeInterface);
- if (runtime_method >= start && runtime_method < limit) {
- return SharedObjectKind::kRuntime;
- }
- return art::LocationIsOnApex(so_name) ? SharedObjectKind::kApexModule
- : SharedObjectKind::kOther;
- }
-
- art::MemoryTypeTable<SharedObjectKind> memory_type_table_;
-
- // Table builder, only valid during BuildCache().
- art::MemoryTypeTable<SharedObjectKind>::Builder* builder_;
-
- // Sanity checking state.
- bool libjavacore_loaded_;
- bool libnativehelper_loaded_;
- bool libopenjdk_loaded_;
-
- static constexpr std::string_view kLibjavacore = "libjavacore.so";
- static constexpr std::string_view kLibnativehelper = "libnativehelper.so";
- static constexpr std::string_view kLibopenjdk = art::kIsDebugBuild ? "libopenjdkd.so"
- : "libopenjdk.so";
-
- DISALLOW_COPY_AND_ASSIGN(CodeRangeCache);
-};
-
-// Whitelisted native callers can resolve method and field id's via JNI. Check the first caller
-// outside of the JNI library who will have called Get(Static)?(Field|Member)ID(). The presence of
-// checked JNI means we need to walk frames as the internal methods can be called directly from an
-// external shared-object or indirectly (via checked JNI) from an external shared-object.
-static inline bool IsWhitelistedNativeCaller() {
- if (!art::kIsTargetBuild) {
- return false;
- }
- for (size_t i = 0; i < kMaxReturnAddressDepth; ++i) {
- void* return_address = GetReturnAddress(i);
- if (return_address == nullptr) {
- return false;
- }
- SharedObjectKind kind = CodeRangeCache::GetSingleton().GetSharedObjectKind(return_address);
- if (kind != SharedObjectKind::kRuntime) {
- return kind == SharedObjectKind::kApexModule;
- }
- }
- return false;
-}
-
} // namespace
namespace art {
@@ -264,13 +85,10 @@
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-// Disable native JNI checking pending stack walk re-evaluation (b/136276414).
-static constexpr bool kNativeJniCheckEnabled = false;
-
template<typename T>
ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kNativeJniCheckEnabled && IsWhitelistedNativeCaller()) {
+ if (hiddenapi::ScopedCorePlatformApiCheck::IsCurrentCallerApproved(self)) {
return false;
}
return hiddenapi::ShouldDenyAccessToMember(
@@ -991,6 +809,7 @@
CHECK_NON_NULL_ARGUMENT(name);
CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return FindMethodID<kEnableIndexIds>(soa, java_class, name, sig, false);
}
@@ -1000,6 +819,7 @@
CHECK_NON_NULL_ARGUMENT(name);
CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return FindMethodID<kEnableIndexIds>(soa, java_class, name, sig, true);
}
@@ -1532,6 +1352,7 @@
CHECK_NON_NULL_ARGUMENT(name);
CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return FindFieldID<kEnableIndexIds>(soa, java_class, name, sig, false);
}
@@ -1541,6 +1362,7 @@
CHECK_NON_NULL_ARGUMENT(name);
CHECK_NON_NULL_ARGUMENT(sig);
ScopedObjectAccess soa(env);
+ hiddenapi::ScopedCorePlatformApiCheck sc;
return FindFieldID<kEnableIndexIds>(soa, java_class, name, sig, true);
}
@@ -3378,16 +3200,6 @@
return reinterpret_cast<JNINativeInterface*>(&gJniSleepForeverStub);
}
-void JniInitializeNativeCallerCheck() {
- // This method should be called only once and before there are multiple runtime threads.
- DCHECK(!CodeRangeCache::GetSingleton().HasCache());
- CodeRangeCache::GetSingleton().BuildCache();
-}
-
-void JniShutdownNativeCallerCheck() {
- CodeRangeCache::GetSingleton().DropCache();
-}
-
} // namespace art
std::ostream& operator<<(std::ostream& os, const jobjectRefType& rhs) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index dbc2f6d..f1b14dc 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -86,6 +86,7 @@
#include "gc/system_weak.h"
#include "handle_scope-inl.h"
#include "hidden_api.h"
+#include "hidden_api_jni.h"
#include "image-inl.h"
#include "instrumentation.h"
#include "intern_table-inl.h"
@@ -95,7 +96,6 @@
#include "jit/profile_saver.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_id_manager.h"
-#include "jni/jni_internal.h"
#include "jni_id_type.h"
#include "linear_alloc.h"
#include "memory_representation.h"
@@ -495,7 +495,7 @@
// elements of WellKnownClasses to be null, see b/65500943.
WellKnownClasses::Clear();
- JniShutdownNativeCallerCheck();
+ hiddenapi::JniShutdownNativeCallerCheck();
}
struct AbortState {
@@ -1873,7 +1873,7 @@
// Having loaded native libraries for Managed Core library, enable field and
// method resolution checks via JNI from native code.
- JniInitializeNativeCallerCheck();
+ hiddenapi::JniInitializeNativeCallerCheck();
VLOG(startup) << "Runtime::InitNativeMethods exiting";
}
diff --git a/runtime/thread.h b/runtime/thread.h
index a8b2b6e..98aaa92 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -964,6 +964,14 @@
is_runtime_thread_ = is_runtime_thread;
}
+ uint32_t CorePlatformApiCookie() {
+ return core_platform_api_cookie_;
+ }
+
+ void SetCorePlatformApiCookie(uint32_t cookie) {
+ core_platform_api_cookie_ = cookie;
+ }
+
// Returns true if the thread is allowed to load java classes.
bool CanLoadClasses() const;
@@ -1797,6 +1805,10 @@
// True if the thread is some form of runtime thread (ex, GC or JIT).
bool is_runtime_thread_;
+ // Set during execution of JNI methods that get field and method id's as part of determining if
+ // the caller is allowed to access all fields and methods in the Core Platform API.
+ uint32_t core_platform_api_cookie_ = 0;
+
friend class Dbg; // For SetStateUnsafe.
friend class gc::collector::SemiSpace; // For getting stack traces.
friend class Runtime; // For CreatePeer.