// Should be the same as dalvik.system.VMRuntime.HIDE_MAXTARGETSDK_P_HIDDEN_APIS, // dalvik.system.VMRuntime.HIDE_MAXTARGETSDK_Q_HIDDEN_APIS, and // dalvik.system.VMRuntime.ALLOW_TEST_API_ACCESS. // Corresponds to bug ids. static constexpr uint64_t kHideMaxtargetsdkPHiddenApis = 149997251; static constexpr uint64_t kHideMaxtargetsdkQHiddenApis = 149994052; static constexpr uint64_t kAllowTestApiAccess = 166236554;
// Should be the same as dalvik.system.VMRuntime.PREVENT_META_REFLECTION_BLOCKLIST_ACCESS. // Corresponds to a bug id. static constexpr uint64_t kPreventMetaReflectionBlocklistAccess = 142365358;
// Set to true if we should always print a warning in logcat for all hidden API accesses, not just // conditionally and unconditionally blocked. This can be set to true for developer preview / beta // builds, but should be false for public release builds. // Note that when flipping this flag, you must also update the expectations of test 674-hiddenapi // as it affects whether or not we warn for unsupported APIs that have been added to the exemptions // list. static constexpr bool kLogAllAccesses = false;
// Exemptions for logcat warning. Following signatures do not produce a warning as app developers // should not be alerted on the usage of these unsupported APIs. See b/154851649. staticconst std::vector<std::string> kWarningExemptions = { "Ljava/nio/Buffer;", "Llibcore/io/Memory;", "Lsun/misc/Unsafe;",
};
// TODO(b/377676642): Fix API annotations and delete this. staticconst std::vector<std::string> kCorePlatformApiExemptions = { // Intra-core APIs that aren't also core platform APIs. These may be used by // the non-updatable ICU module and hence are effectively de-facto core // platform APIs. "Ldalvik/annotation/compat/VersionCodes;", "Ldalvik/annotation/optimization/ReachabilitySensitive;", "Ldalvik/system/BlockGuard/Policy;->onNetwork", "Ljava/nio/charset/CharsetEncoder;-><init>(Ljava/nio/charset/Charset;FF[BZ)V", "Ljava/security/spec/ECParameterSpec;->getCurveName", "Ljava/security/spec/ECParameterSpec;->setCurveName", "Llibcore/api/CorePlatformApi;", "Llibcore/io/AsynchronousCloseMonitor;", "Llibcore/util/NonNull;", "Llibcore/util/Nullable;", "Lsun/security/util/DerEncoder;", "Lsun/security/x509/AlgorithmId;->derEncode", "Lsun/security/x509/AlgorithmId;->get", // These are new system module APIs that are accessed unflagged (cf. // b/400041178 and b/400041556). "Ldalvik/system/VMDebug;->setCurrentProcessName", "Ldalvik/system/VMDebug;->addApplication", "Ldalvik/system/VMDebug;->removeApplication", "Ldalvik/system/VMDebug;->setUserId", "Ldalvik/system/VMDebug;->setWaitingForDebugger", // frameworks/base/core/jni/eventlog_helper.h accesses these fields directly // via JNI during initialisation (typically in the zygote). "Ljava/lang/Integer;->value:I", "Ljava/lang/Long;->value:J", "Ljava/lang/Float;->value:F", // More JNI accesses from libandroid_runtime.so in the platform. "Ljava/io/FileDescriptor;-><init>(I)V", "Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V", "Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V", // Other de-facto core platform APIs "Landroid/security/net/config/RootTrustManager;->isSameTrustConfiguration(Ljava/lang/" "String;Ljava/lang/String;)Z",
};
std::ostream& operator<<(std::ostream& os, EnforcementPolicy policy) { switch (policy) { case EnforcementPolicy::kDisabled:
os << "disabled"; break; case EnforcementPolicy::kJustWarn:
os << "just-warn"; break; case EnforcementPolicy::kEnabled:
os << "enabled"; break;
} return os;
}
std::ostream& operator<<(std::ostream& os, AccessMethod value) { switch (value) { case AccessMethod::kCheck:
os << "<check>"; break; case AccessMethod::kCheckWithPolicy:
os << "<check-with-policy>"; break; case AccessMethod::kReflection:
os << "reflection"; break; case AccessMethod::kJNI:
os << "JNI"; break; case AccessMethod::kLinking:
os << "linking"; break;
} return os;
}
std::ostream& operator<<(std::ostream& os, Domain domain) { switch (domain) { case Domain::kCorePlatform:
os << "core-platform"; break; case Domain::kPlatform:
os << "platform"; break; case Domain::kApplication:
os << "app"; break;
} return os;
}
static std::optional<Domain> DetermineDomainForApexLocation(const std::string& dex_location) { // If running with APEX, check `path` against known APEX locations. // These checks will be skipped on target buildbots where ANDROID_ART_ROOT // is set to "/system". if (ArtModuleRootDistinctFromAndroidRoot()) { if (LocationIsOnArtModule(dex_location) || LocationIsOnConscryptModule(dex_location)) { return Domain::kCorePlatform;
}
if (LocationIsOnApex(dex_location)) { return Domain::kPlatform;
}
}
if (LocationIsOnSystemFramework(dex_location) || LocationIsOnSystemExtFramework(dex_location)) { return Domain::kPlatform;
}
if (class_loader.IsNull()) { if (kIsTargetBuild && !kIsTargetLinux) { // This is unexpected only when running on Android.
LOG(WARNING) << "hiddenapi: DexFile " << dex_location
<< " is in boot class path but is not in a known location";
} return Domain::kPlatform;
}
return Domain::kApplication;
}
AccessContext AccessContext::FromNativeCaller(void* native_caller_addr) { #ifdef ART_TARGET_ANDROID if (std::optional<std::string> opt_dso_name = FindDsoForNativeCaller(native_caller_addr);
opt_dso_name.has_value()) {
std::string dso_name = std::move(opt_dso_name.value()); if (std::optional<Domain> d = DetermineDomainForApexLocation(dso_name); d.has_value()) { return AccessContext(nullptr, nullptr, native_caller_addr, d.value());
}
LOG(INFO) << "hiddenapi: DSO couldn't be determined for caller " << native_caller_addr; return AccessContext(/*is_trusted=*/false);
#else// !ART_TARGET_ANDROID // In host tests all APEX and system .so libs end up in the same directory // (typically out/host/linux-x86/lib64), making it tedious to tell platform and // core-platform domains apart. Leave the caller unidentified to use fallback // code paths, but it means some tests may not behave correctly on host. // // With some work we could identify the .so libs in the tests themselves as // belonging to the app domain (e.g. out/host/linux-x86/nativetest(64), // out/host/linux-x86/testcases, and /tmp/art/test for run tests), but that's // not done yet.
UNUSED(native_caller_addr);
VLOG(hiddenapi) << "hiddenapi: Skipping native caller check on host"; return AccessContext(/*is_trusted=*/false); #endif
}
// Assign the domain unless a more permissive domain has already been assigned. // This may happen when DexFile is initialized as trusted. if (IsDomainAtLeastAsTrustedAs(dex_domain, dex_file.GetHiddenapiDomain())) {
dex_file.SetHiddenapiDomain(dex_domain);
}
}
void InitializeCorePlatformApiPrivateFields() { // The following fields in WellKnownClasses correspond to private fields in the Core Platform // API that cannot be otherwise expressed and propagated through tooling (b/144502743).
ArtField* private_core_platform_api_fields[] = {
WellKnownClasses::java_io_FileDescriptor_descriptor,
WellKnownClasses::java_nio_Buffer_address,
WellKnownClasses::java_nio_Buffer_elementSizeShift,
WellKnownClasses::java_nio_Buffer_limit,
WellKnownClasses::java_nio_Buffer_position,
};
staticbool EnableNativeCallerCheckForApp() {
CHECK(com::android::art::flags::hiddenapi_jni_api_callers());
uint32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion(); // Enable if the target SDK is higher than 36 (Android 16). Also enable if the // target SDK version is not set, e.g. in the zygote. return IsSdkVersionUnsetOrMoreThan(target_sdk_version, SdkVersion::kB);
}
AccessContext& GetJavaCallerContext() REQUIRES_SHARED(Locks::mutator_lock_) { if (!java_caller_context.has_value()) {
ObjPtr<mirror::Class> caller = GetCallingClass(self, /* num_frames= */ 1); // If the calling class cannot be determined, e.g. unattached threads, we // conservatively assume the caller is trusted.
java_caller_context.emplace(caller.IsNull() ? AccessContext(/* is_trusted= */ true)
: AccessContext(caller));
} return java_caller_context.value();
}
} ctx{.self = self, .native_caller_addr = native_caller_addr};
if (EnableHiddenapiPlatformEnforcement() &&
(!com::android::art::flags::hiddenapi_jni_api_callers() ||
!EnableNativeCallerCheckForApp())) { // Special case to avoid false alarms when accesses from platform to // core-platform are prohibited: If the topmost java frame is in platform // but it has called into native code in an app, then it's possible that the // app has access to APIs that the platform hasn't (e.g. if the app has an // old and permissive target SDK level). // // That's not a problem for apps with a recent enough target SDK level where // we always check the native caller in the standard code path below, but // otherwise we need to check for that specific situation.
AccessMethod check_only_method =
IsCheckOnlyMethod(access_kind) ? access_kind : AccessMethod::kCheckWithPolicy; if (ShouldDenyAccessToMember(
member,
[&ctx]() REQUIRES_SHARED(Locks::mutator_lock_) {
AccessContext& context = ctx.GetJavaCallerContext();
VLOG(hiddenapi) << "hiddenapi: Managed JNI caller " << context << " from "
<< context.GetDomain() << " (special case)"; return context;
},
check_only_method) &&
ctx.java_caller_context.value().GetDomain() == Domain::kPlatform) { // The java caller is in platform and has been denied, so check the native caller.
AccessContext& context = ctx.GetNativeCallerContext();
VLOG(hiddenapi) << "hiddenapi: Native JNI caller " << context << " from "
<< context.GetDomain() << " (special case)"; if (context.GetNativeCallerAddr() == nullptr || context.IsApplicationDomain()) { // If the native caller either could not be identified or it's in an // app, then either the app may have access to an API that platform // doesn't, or it may be an app using a method to circumvent hidden API // checks. In either case we need to be conservative and allow the // access for the sake of app compat, because in this situation we must // not regress on that. returnfalse;
}
} // Fall through to the standard check in all other cases. The resolved // native and java callers have been cached in ctx so it should be fast.
}
// Standard code path for the hiddenapi check. return ShouldDenyAccessToMember(
member,
[&ctx]() REQUIRES_SHARED(Locks::mutator_lock_) { if (com::android::art::flags::hiddenapi_jni_api_callers()) { // Construct the context from the native caller address.
AccessContext& context = ctx.GetNativeCallerContext();
if (context.GetNativeCallerAddr() != nullptr) { switch (context.GetDomain()) { case Domain::kApplication: // If the native caller is an app then it should only be used if // the app's target SDK version is recent enough. if (EnableNativeCallerCheckForApp()) {
VLOG(hiddenapi) << "hiddenapi: Native JNI caller " << context << " from "
<< context.GetDomain() << " (target_sdk_version="
<< Runtime::Current()->GetTargetSdkVersion() << ")"; return context;
}
VLOG(hiddenapi) << "hiddenapi: Native JNI caller " << context << " from "
<< context.GetDomain() << " ignored"
<< " (target_sdk_version="
<< Runtime::Current()->GetTargetSdkVersion() << ")"; break;
case Domain::kPlatform: // If the native caller is platform then it should only be used if // the SDK level is recent enough. if (EnableHiddenapiPlatformEnforcement()) {
VLOG(hiddenapi) << "hiddenapi: Native JNI caller " << context << " from "
<< context.GetDomain(); return context;
}
VLOG(hiddenapi) << "hiddenapi: Native JNI caller " << context << " from "
<< context.GetDomain() << " ignored"; break;
case Domain::kCorePlatform: // The native caller is essentially ourselves, and this is the most // permissive domain so it's fine.
VLOG(hiddenapi) << "hiddenapi: Native JNI caller " << context << " from "
<< context.GetDomain(); return context;
}
}
}
// If we couldn't (or shouldn't) determine the context from the native // caller address then look at the first calling class on the Java stack. // This is the legacy approach, and it's also better than nothing if a DSO // cannot be identified (e.g. for generated code).
AccessContext& context = ctx.GetJavaCallerContext();
VLOG(hiddenapi) << "hiddenapi: Managed JNI caller " << context << " from "
<< context.GetDomain(); return context;
},
access_kind);
}
AccessContext GetReflectionCallerAccessContext(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Walk the stack and find the first frame not from java.lang.Class, // java.lang.invoke or java.lang.reflect. This is very expensive. // Save this till the last. struct FirstExternalCallerVisitor : public StackVisitor { explicit FirstExternalCallerVisitor(Thread* thread)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
caller(nullptr) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* m = GetMethod(); if (m == nullptr) { // Attached native thread. Assume this is *not* boot class path.
VLOG(hiddenapi) << "hiddenapi: Stack walk: " << "no method - giving up";
caller = nullptr; returnfalse;
} elseif (m->IsRuntimeMethod()) { // Internal runtime method, continue walking the stack.
VLOG(hiddenapi) << "hiddenapi: Stack walk: " << "internal method: " << m->PrettyMethod(); returntrue;
}
// MethodHandles.makeIdentity is doing findStatic to find hidden methods, // where reflection is used. if (m == WellKnownClasses::java_lang_invoke_MethodHandles_makeIdentity) {
VLOG(hiddenapi) << "hiddenapi: Stack walk: " << m->PrettyMethod() << " - end"; returnfalse;
}
// Check classes in the java.lang.invoke package. At the time of writing, the // classes of interest are MethodHandles and MethodHandles.Lookup, but this // is subject to change so conservatively cover the entire package. // NB Static initializers within java.lang.invoke are permitted and do not // need further stack inspection.
ObjPtr<mirror::Class> lookup_class = GetClassRoot<mirror::MethodHandlesLookup>(); if ((declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class)) &&
!m->IsClassInitializer()) {
VLOG(hiddenapi) << "hiddenapi: Stack walk: " << "java.lang.invoke package: "
<< m->PrettyMethod(); returntrue;
} // Check for classes in the java.lang.reflect package, except for java.lang.reflect.Proxy. // java.lang.reflect.Proxy does its own hidden api checks (https://r.android.com/915496), // and walking over this frame would cause a null pointer dereference // (e.g. in 691-hiddenapi-proxy).
ObjPtr<mirror::Class> proxy_class = GetClassRoot<mirror::Proxy>();
CompatFramework& compat_framework = Runtime::Current()->GetCompatFramework(); if (declaring_class->IsInSamePackage(proxy_class) && declaring_class != proxy_class) { if (compat_framework.IsChangeEnabled(kPreventMetaReflectionBlocklistAccess)) {
VLOG(hiddenapi) << "hiddenapi: Stack walk: " << "java.lang.reflect package: "
<< m->PrettyMethod(); returntrue;
}
}
}
// Construct AccessContext from the calling class found on the stack. // If the calling class cannot be determined, e.g. unattached threads, // we conservatively assume the caller is trusted.
ObjPtr<mirror::Class> caller =
(visitor.caller == nullptr) ? nullptr : visitor.caller->GetDeclaringClass();
AccessContext context =
caller.IsNull() ? AccessContext(/* is_trusted= */ true) : AccessContext(caller);
VLOG(hiddenapi) << "hiddenapi: Reflection caller " << context << " from " << context.GetDomain(); return context;
}
namespace detail {
// Do not change the values of items in this enum, as they are written to the // event log for offline analysis. Any changes will interfere with that analysis. enum AccessContextFlags { // Accessed member is a field if this bit is set, else a method
kMemberIsField = 1 << 0, // Indicates if access was denied to the member, instead of just printing a warning.
kAccessDenied = 1 << 1,
};
MemberSignature::MemberSignature(ArtField* field) { // Note: `ArtField::GetDeclaringClassDescriptor()` does not support proxy classes.
class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
member_name_ = field->GetNameView();
type_signature_ = field->GetTypeDescriptorView();
type_ = kField;
}
MemberSignature::MemberSignature(ArtMethod* method) {
DCHECK(method == method->GetInterfaceMethodIfProxy(kRuntimePointerSize))
<< "Caller should have replaced proxy method with interface method";
class_name_ = method->GetDeclaringClassDescriptorView();
member_name_ = method->GetNameView();
type_signature_ = method->GetSignature().ToString();
type_ = kMethod;
}
bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const {
size_t pos = 0; for (constchar* part : GetSignatureParts()) {
size_t count = std::min(prefix.length() - pos, strlen(part)); if (prefix.compare(pos, count, part, 0, count) == 0) {
pos += count;
} else { returnfalse;
}
} // We have a complete match if all parts match (we exit the loop without // returning) AND we've matched the whole prefix. return pos == prefix.length();
}
bool MemberSignature::DoesPrefixMatchAny(const std::vector<std::string>& exemptions) { for (const std::string& exemption : exemptions) { if (DoesPrefixMatch(exemption)) { returntrue;
}
} returnfalse;
}
void MemberSignature::Dump(std::ostream& os) const { for (constchar* part : GetSignatureParts()) {
os << part;
}
}
void MemberSignature::LogAccessToLogcat(AccessMethod access_method,
ApiList api_list, bool access_denied,
uint32_t runtime_flags, const AccessContext& caller_context, const AccessContext& callee_context,
EnforcementPolicy policy) {
CHECK(access_method == AccessMethod::kReflection || access_method == AccessMethod::kJNI ||
access_method == AccessMethod::kLinking)
<< access_method; static std::atomic<uint64_t> logged_access_count_ = 0; if (!gLogVerbosity.hiddenapi && logged_access_count_ > kMaxLogAccessesToLogcat) { return;
}
LOG(access_denied ? (policy == EnforcementPolicy::kEnabled ? ERROR : WARNING) : INFO)
<< "hiddenapi: Accessing hidden " << (type_ == kField ? "field " : "method ")
<< Dumpable<MemberSignature>(*this)
<< " (runtime_flags=" << FormatHiddenApiRuntimeFlags(runtime_flags)
<< ", domain=" << callee_context.GetDomain() << ", api=" << api_list << ") from "
<< caller_context << " (domain=" << caller_context.GetDomain()
<< ", TargetSdkVersion=" << Runtime::Current()->GetTargetSdkVersion() << ") using "
<< access_method << (access_denied ? ": denied" : ": allowed"); if (access_denied && api_list.IsTestApi()) { // see b/177047045 for more details about test api access getting denied
LOG(WARNING) << "hiddenapi: If this is a platform test consider enabling "
<< "VMRuntime.ALLOW_TEST_API_ACCESS change id for this package.";
} if (!gLogVerbosity.hiddenapi && logged_access_count_ >= kMaxLogAccessesToLogcat) {
LOG(WARNING) << "hiddenapi: Reached maximum number of hidden api access messages.";
}
++logged_access_count_;
}
void MemberSignature::LogAccessToEventLog(uint32_t sampled_value,
AccessMethod access_method, bool access_denied) { #ifdef ART_TARGET_ANDROID if (IsCheckOnlyMethod(access_method) || access_method == AccessMethod::kLinking) { // Checks do not correspond to actual accesses, so should be ignored. // Linking warnings come from static analysis/compilation of the bytecode // and can contain false positives (i.e. code that is never run). Hence we // choose to not log those either in the event log. return;
}
Runtime* runtime = Runtime::Current(); if (runtime->IsAotCompiler()) { return;
}
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<2u> hs(soa.Self());
Handle<mirror::String> package_str =
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), package_name.c_str())); if (soa.Self()->IsExceptionPending()) {
soa.Self()->ClearException();
LOG(ERROR) << "hiddenapi: Unable to allocate string for package name which called hidden api";
}
Handle<mirror::String> signature_jstr =
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), signature_str.str().c_str())); if (soa.Self()->IsExceptionPending()) {
soa.Self()->ClearException();
LOG(ERROR) << "hiddenapi: Unable to allocate string for hidden api method signature";
}
WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed
->InvokeStatic<'V', 'I', 'L', 'L', 'I', 'Z'>(soa.Self(), static_cast<jint>(sampled_value),
package_str.Get(),
signature_jstr.Get(), static_cast<jint>(access_method),
access_denied); if (soa.Self()->IsExceptionPending()) {
soa.Self()->ClearException();
LOG(ERROR) << "hiddenapi: Unable to report hidden api usage";
} #else
UNUSED(sampled_value);
UNUSED(access_method);
UNUSED(access_denied); #endif
}
void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) { if (access_method != AccessMethod::kReflection && access_method != AccessMethod::kJNI) { // We can only up-call into Java during reflection and JNI down-calls. return;
}
// If the consumer is non-null, we call back to it to let it know that we // have encountered an API that's in one of our lists. if (consumer_object != nullptr) {
std::ostringstream member_signature_str;
Dump(member_signature_str);
Handle<mirror::String> signature_str = hs.NewHandle(
mirror::String::AllocFromModifiedUtf8(soa.Self(), member_signature_str.str().c_str())); // FIXME: Handle OOME. For now, crash immediatelly (do not continue with a pending exception).
CHECK(signature_str != nullptr);
// Call through to Consumer.accept(String memberSignature);
WellKnownClasses::java_util_function_Consumer_accept->InvokeInterface<'V', 'L'>(
soa.Self(), consumer_object.Get(), signature_str.Get());
}
}
}
template <typename T> static ALWAYS_INLINE void MaybeUpdateAccessFlags(Runtime* runtime, T* member, uint32_t flag)
REQUIRES_SHARED(Locks::mutator_lock_) { // Update the access flags unless: // (a) `member` is an intrinsic // (b) this is AOT compiler, as we do not want the updated access flags in the boot/app image // (c) deduping warnings has been explicitly switched off. if (CanUpdateRuntimeFlags(member) && !runtime->IsAotCompiler() &&
runtime->ShouldDedupeHiddenApiWarnings()) {
member->SetAccessFlags(member->GetAccessFlags() | flag);
}
}
static ALWAYS_INLINE uint32_t GetMemberDexIndex(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) { // Use the non-obsolete method to avoid DexFile mismatch between // the method index and the declaring class. return method->GetNonObsoleteMethod()->GetDexMethodIndex();
}
ObjPtr<mirror::Class> declaring_class = member->GetDeclaringClass();
DCHECK(!declaring_class.IsNull()) << "Attempting to access a runtime method";
ApiList flags = ApiList::Invalid();
// Check if the declaring class has ClassExt allocated. If it does, check if // the pre-JVMTI redefine dex file has been set to determine if the declaring // class has been JVMTI-redefined.
ObjPtr<mirror::ClassExt> ext(declaring_class->GetExtData()); const DexFile* original_dex = ext.IsNull() ? nullptr : ext->GetPreRedefineDexFile(); if (LIKELY(original_dex == nullptr)) { // Class is not redefined. Find the class def, iterate over its members and // find the entry corresponding to this `member`. const dex::ClassDef* class_def = declaring_class->GetClassDef(); if (class_def == nullptr) { // ClassDef is not set for proxy classes. Only their fields can ever be inspected.
DCHECK(declaring_class->IsProxyClass())
<< "Only proxy classes are expected not to have a class def";
DCHECK(kMemberIsField)
<< "Interface methods should be inspected instead of proxy class methods";
flags = ApiList::Unsupported();
} else {
uint32_t member_index = GetMemberDexIndex(member); auto fn_visit = [&](const AccessorType& dex_member) { if (dex_member.GetIndex() == member_index) {
flags = ApiList::FromDexFlags(dex_member.GetHiddenapiFlags());
}
};
VisitMembers(declaring_class->GetDexFile(), *class_def, fn_visit);
}
} else { // Class was redefined using JVMTI. We have a pointer to the original dex file // and the class def index of this class in that dex file, but the field/method // indices are lost. Iterate over all members of the class def and find the one // corresponding to this `member` by name and type string comparison. // This is obviously very slow, but it is only used when non-exempt code tries // to access a hidden member of a JVMTI-redefined class.
uint16_t class_def_idx = ext->GetPreRedefineClassDefIndex();
DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16); const dex::ClassDef& original_class_def = original_dex->GetClassDef(class_def_idx);
MemberSignature member_signature(member); auto fn_visit = [&](const AccessorType& dex_member) {
MemberSignature cur_signature(dex_member); if (member_signature.MemberNameAndTypeMatch(cur_signature)) {
DCHECK(member_signature.Equals(cur_signature));
flags = ApiList::FromDexFlags(dex_member.GetHiddenapiFlags());
}
};
VisitMembers(*original_dex, original_class_def, fn_visit);
}
CHECK(flags.IsValid()) << "Could not find hiddenapi flags for "
<< Dumpable<MemberSignature>(MemberSignature(member)); return flags.GetDexFlags();
}
template <typename T> bool HandleCorePlatformApiViolation(T* member,
ApiList api_list,
uint32_t runtime_flags, const AccessContext& caller_context, const AccessContext& callee_context,
AccessMethod access_method,
EnforcementPolicy policy) {
DCHECK(policy != EnforcementPolicy::kDisabled)
<< "Should never enter this function when access checks are completely disabled";
if (access_method == AccessMethod::kCheck) { // Always return true for internal checks, so the current enforcement policy // won't affect the caller. returntrue;
}
// If policy is set to just warn, add kAccCorePlatformApi to access flags of // `member` to avoid reporting the violation again next time. if (policy == EnforcementPolicy::kJustWarn) {
MaybeUpdateAccessFlags(Runtime::Current(), member, kAccCorePlatformApi);
}
}
// Deny access if enforcement is enabled. return policy == EnforcementPolicy::kEnabled;
}
EnforcementPolicy hiddenApiPolicy = runtime->GetHiddenApiEnforcementPolicy();
DCHECK(hiddenApiPolicy != EnforcementPolicy::kDisabled)
<< "Should never enter this function when access checks are completely disabled";
MemberSignature member_signature(member);
// Check for an exemption first. Exempted APIs are treated as SDK. if (member_signature.DoesPrefixMatchAny(runtime->GetHiddenApiExemptions())) { // Avoid re-examining the exemption list next time. // Note this results in no warning for the member, which seems like what one would expect. // Exemptions effectively adds new members to the public API list.
MaybeUpdateAccessFlags(runtime, member, kAccPublicApi); returnfalse;
}
if (!IsCheckOnlyMethod(access_method)) { // Warn if blocked signature is being accessed or it is not exempted. if (deny_access || !member_signature.DoesPrefixMatchAny(kWarningExemptions)) { // Print a log message with information about this class member access. // We do this if we're about to deny access, or the app is debuggable. if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
member_signature.LogAccessToLogcat(access_method,
api_list,
deny_access,
runtime_flags,
caller_context,
callee_context,
hiddenApiPolicy);
}
// If there is a StrictMode listener, notify it about this violation.
member_signature.NotifyHiddenApiListener(access_method);
}
// If event log sampling is enabled, report this violation. if (kIsTargetBuild && !kIsTargetLinux) {
uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate(); // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small"); if (eventLogSampleRate != 0) { const uint32_t sampled_value = static_cast<uint32_t>(std::rand()) & 0xffff; if (sampled_value <= eventLogSampleRate) {
member_signature.LogAccessToEventLog(sampled_value, access_method, deny_access);
}
}
}
// If this access was not denied, flag member as SDK and skip // the warning the next time the member is accessed. Don't update for // non-debuggable apps as this has a memory cost. if (!deny_access && runtime->IsJavaDebuggable()) {
MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
}
}
// First check if we have an explicit sdk checker installed that should be used to // verify access. If so, make the decision based on it. // // This is used during off-device AOT compilation which may want to generate verification // metadata only for a specific list of public SDKs. Note that the check here is made // based on descriptor equality and it's aim to further restrict a symbol that would // otherwise be resolved. // // The check only applies to boot classpaths dex files.
Runtime* runtime = Runtime::Current(); if (UNLIKELY(runtime->IsAotCompiler())) { if (member->GetDeclaringClass()->IsBootStrapClassLoaded() &&
runtime->GetClassLinker()->DenyAccessBasedOnPublicSdk(member)) { returntrue;
}
}
// Get the runtime flags encoded in member's access flags. // Note: this works for proxy methods because they inherit access flags from their // respective interface methods. const uint32_t runtime_flags = GetRuntimeFlags(member);
// Exit early if member is public API. This flag is also set for non-boot class // path fields/methods. if ((runtime_flags & kAccPublicApi) != 0) { returnfalse;
}
// Determine which domain the caller and callee belong to. // This can be *very* expensive. This is why ShouldDenyAccessToMember // should not be called on every individual access. const AccessContext caller_context = fn_get_access_context();
caller_context.GetClass().AssertValid(); const AccessContext callee_context(member->GetDeclaringClass());
// Non-boot classpath callers should have exited early.
DCHECK(!callee_context.IsApplicationDomain());
// Check if the caller is always allowed to access members in the callee context. if (caller_context.CanAlwaysAccess(callee_context)) { returnfalse;
}
// Check if this is platform accessing core platform. We may warn if `member` is // not part of core platform API. switch (caller_context.GetDomain()) { case Domain::kApplication: {
DCHECK(!callee_context.IsApplicationDomain());
// Exit early if access checks are completely disabled.
EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kDisabled) { returnfalse;
}
// If this is a proxy method, look at the interface method instead.
member = detail::GetInterfaceMemberIfProxy(member);
// Decode hidden API access flags from the dex file. // This is an O(N) operation scaling with the number of fields/methods // in the class. Only do this on slow path and only do it once.
ApiList api_list = ApiList::FromDexFlags(detail::GetDexFlags(member));
DCHECK(api_list.IsValid());
// Member is hidden and caller is not exempted. Enter slow path. return detail::ShouldDenyAccessToMemberImpl(
member, api_list, runtime_flags, caller_context, callee_context, access_method);
}
case Domain::kPlatform: {
DCHECK(callee_context.GetDomain() == Domain::kCorePlatform);
// Member is part of core platform API. Accessing it is allowed. if ((runtime_flags & kAccCorePlatformApi) != 0) { returnfalse;
}
// Allow access if access checks are disabled.
EnforcementPolicy policy = runtime->GetCorePlatformApiEnforcementPolicy(); if (policy == EnforcementPolicy::kDisabled) { returnfalse;
}
// If this is a proxy method, look at the interface method instead.
member = detail::GetInterfaceMemberIfProxy(member);
// Decode hidden API access flags from the dex file. This is a slow path, // like in the kApplication case above.
ApiList api_list = ApiList::FromDexFlags(detail::GetDexFlags(member));
DCHECK(api_list.IsValid());
// Max target SDK versions don't matter for platform callers, but they may // still depend on unsupported APIs. Let's compare against the "max" SDK // version to only allow that (and also proper SDK APIs, but they are // typically combined with kCorePlatformApi already). if (api_list.GetMaxAllowedSdkVersion() == SdkVersion::kMax) { // Allow access and attempt to update the access flags to avoid // re-examining the dex flags next time.
detail::MaybeUpdateAccessFlags(runtime, member, kAccCorePlatformApi); returnfalse;
}
// Check for exemptions. // TODO(b/377676642): Fix API annotations and delete this.
detail::MemberSignature member_signature(member); if (member_signature.DoesPrefixMatchAny(kCorePlatformApiExemptions)) { // Avoid re-examining the exemption list next time.
detail::MaybeUpdateAccessFlags(runtime, member, kAccCorePlatformApi); returnfalse;
}
// Access checks are not disabled, report the violation. // This may also add kAccCorePlatformApi to the access flags of `member` // so as to not warn again on next access. return detail::HandleCorePlatformApiViolation(
member, api_list, runtime_flags, caller_context, callee_context, access_method, policy);
}
case Domain::kCorePlatform: {
LOG(FATAL) << "CorePlatform domain should be allowed to access all domains";
UNREACHABLE();
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.