Home | History | Annotate | Download | only in runtime
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <metricslogger/metrics_logger.h>
     18 
     19 #include "hidden_api.h"
     20 
     21 #include <nativehelper/scoped_local_ref.h>
     22 
     23 #include "base/dumpable.h"
     24 #include "thread-current-inl.h"
     25 #include "well_known_classes.h"
     26 
     27 using android::metricslogger::ComplexEventLogger;
     28 using android::metricslogger::ACTION_HIDDEN_API_ACCESSED;
     29 using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD;
     30 using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED;
     31 using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE;
     32 
     33 namespace art {
     34 namespace hiddenapi {
     35 
     36 // Set to true if we should always print a warning in logcat for all hidden API accesses, not just
     37 // dark grey and black. This can be set to true for developer preview / beta builds, but should be
     38 // false for public release builds.
     39 // Note that when flipping this flag, you must also update the expectations of test 674-hiddenapi
     40 // as it affects whether or not we warn for light grey APIs that have been added to the exemptions
     41 // list.
     42 static constexpr bool kLogAllAccesses = false;
     43 
     44 static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
     45   switch (value) {
     46     case kNone:
     47       LOG(FATAL) << "Internal access to hidden API should not be logged";
     48       UNREACHABLE();
     49     case kReflection:
     50       os << "reflection";
     51       break;
     52     case kJNI:
     53       os << "JNI";
     54       break;
     55     case kLinking:
     56       os << "linking";
     57       break;
     58   }
     59   return os;
     60 }
     61 
     62 static constexpr bool EnumsEqual(EnforcementPolicy policy, HiddenApiAccessFlags::ApiList apiList) {
     63   return static_cast<int>(policy) == static_cast<int>(apiList);
     64 }
     65 
     66 // GetMemberAction-related static_asserts.
     67 static_assert(
     68     EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, HiddenApiAccessFlags::kDarkGreylist) &&
     69     EnumsEqual(EnforcementPolicy::kBlacklistOnly, HiddenApiAccessFlags::kBlacklist),
     70     "Mismatch between EnforcementPolicy and ApiList enums");
     71 static_assert(
     72     EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
     73     EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
     74     "EnforcementPolicy values ordering not correct");
     75 
     76 namespace detail {
     77 
     78 MemberSignature::MemberSignature(ArtField* field) {
     79   class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
     80   member_name_ = field->GetName();
     81   type_signature_ = field->GetTypeDescriptor();
     82   type_ = kField;
     83 }
     84 
     85 MemberSignature::MemberSignature(ArtMethod* method) {
     86   // If this is a proxy method, print the signature of the interface method.
     87   method = method->GetInterfaceMethodIfProxy(
     88       Runtime::Current()->GetClassLinker()->GetImagePointerSize());
     89 
     90   class_name_ = method->GetDeclaringClass()->GetDescriptor(&tmp_);
     91   member_name_ = method->GetName();
     92   type_signature_ = method->GetSignature().ToString();
     93   type_ = kMethod;
     94 }
     95 
     96 inline std::vector<const char*> MemberSignature::GetSignatureParts() const {
     97   if (type_ == kField) {
     98     return { class_name_.c_str(), "->", member_name_.c_str(), ":", type_signature_.c_str() };
     99   } else {
    100     DCHECK_EQ(type_, kMethod);
    101     return { class_name_.c_str(), "->", member_name_.c_str(), type_signature_.c_str() };
    102   }
    103 }
    104 
    105 bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const {
    106   size_t pos = 0;
    107   for (const char* part : GetSignatureParts()) {
    108     size_t count = std::min(prefix.length() - pos, strlen(part));
    109     if (prefix.compare(pos, count, part, 0, count) == 0) {
    110       pos += count;
    111     } else {
    112       return false;
    113     }
    114   }
    115   // We have a complete match if all parts match (we exit the loop without
    116   // returning) AND we've matched the whole prefix.
    117   return pos == prefix.length();
    118 }
    119 
    120 bool MemberSignature::IsExempted(const std::vector<std::string>& exemptions) {
    121   for (const std::string& exemption : exemptions) {
    122     if (DoesPrefixMatch(exemption)) {
    123       return true;
    124     }
    125   }
    126   return false;
    127 }
    128 
    129 void MemberSignature::Dump(std::ostream& os) const {
    130   for (const char* part : GetSignatureParts()) {
    131     os << part;
    132   }
    133 }
    134 
    135 void MemberSignature::WarnAboutAccess(AccessMethod access_method,
    136                                       HiddenApiAccessFlags::ApiList list) {
    137   LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
    138                << Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
    139 }
    140 // Convert an AccessMethod enum to a value for logging from the proto enum.
    141 // This method may look odd (the enum values are current the same), but it
    142 // prevents coupling the internal enum to the proto enum (which should never
    143 // be changed) so that we are free to change the internal one if necessary in
    144 // future.
    145 inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
    146   switch (access_method) {
    147     case kNone:
    148       return android::metricslogger::ACCESS_METHOD_NONE;
    149     case kReflection:
    150       return android::metricslogger::ACCESS_METHOD_REFLECTION;
    151     case kJNI:
    152       return android::metricslogger::ACCESS_METHOD_JNI;
    153     case kLinking:
    154       return android::metricslogger::ACCESS_METHOD_LINKING;
    155     default:
    156       DCHECK(false);
    157   }
    158 }
    159 
    160 void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
    161   if (access_method == kLinking || access_method == kNone) {
    162     // Linking warnings come from static analysis/compilation of the bytecode
    163     // and can contain false positives (i.e. code that is never run). We choose
    164     // not to log these in the event log.
    165     // None does not correspond to actual access, so should also be ignored.
    166     return;
    167   }
    168   ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
    169   log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
    170   if (action_taken == kDeny) {
    171     log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
    172   }
    173   const std::string& package_name = Runtime::Current()->GetProcessPackageName();
    174   if (!package_name.empty()) {
    175     log_maker.SetPackageName(package_name);
    176   }
    177   std::ostringstream signature_str;
    178   Dump(signature_str);
    179   log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str());
    180   log_maker.Record();
    181 }
    182 
    183 static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
    184   return true;
    185 }
    186 
    187 static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtMethod* method) {
    188   return !method->IsIntrinsic();
    189 }
    190 
    191 template<typename T>
    192 static ALWAYS_INLINE void MaybeWhitelistMember(Runtime* runtime, T* member)
    193     REQUIRES_SHARED(Locks::mutator_lock_) {
    194   if (CanUpdateMemberAccessFlags(member) && runtime->ShouldDedupeHiddenApiWarnings()) {
    195     member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
    196         member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
    197   }
    198 }
    199 
    200 template<typename T>
    201 Action GetMemberActionImpl(T* member,
    202                            HiddenApiAccessFlags::ApiList api_list,
    203                            Action action,
    204                            AccessMethod access_method) {
    205   DCHECK_NE(action, kAllow);
    206 
    207   // Get the signature, we need it later.
    208   MemberSignature member_signature(member);
    209 
    210   Runtime* runtime = Runtime::Current();
    211 
    212   // Check for an exemption first. Exempted APIs are treated as white list.
    213   // We only do this if we're about to deny, or if the app is debuggable. This is because:
    214   // - we only print a warning for light greylist violations for debuggable apps
    215   // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
    216   // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
    217   //   possible.
    218   const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable();
    219   if (shouldWarn || action == kDeny) {
    220     if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
    221       action = kAllow;
    222       // Avoid re-examining the exemption list next time.
    223       // Note this results in no warning for the member, which seems like what one would expect.
    224       // Exemptions effectively adds new members to the whitelist.
    225       MaybeWhitelistMember(runtime, member);
    226       return kAllow;
    227     }
    228 
    229     if (access_method != kNone) {
    230       // Print a log message with information about this class member access.
    231       // We do this if we're about to block access, or the app is debuggable.
    232       member_signature.WarnAboutAccess(access_method, api_list);
    233     }
    234   }
    235 
    236   if (kIsTargetBuild) {
    237     uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
    238     // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
    239     static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
    240     if (eventLogSampleRate != 0 &&
    241         (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
    242       member_signature.LogAccessToEventLog(access_method, action);
    243     }
    244   }
    245 
    246   if (action == kDeny) {
    247     // Block access
    248     return action;
    249   }
    250 
    251   // Allow access to this member but print a warning.
    252   DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
    253 
    254   if (access_method != kNone) {
    255     // Depending on a runtime flag, we might move the member into whitelist and
    256     // skip the warning the next time the member is accessed.
    257     MaybeWhitelistMember(runtime, member);
    258 
    259     // If this action requires a UI warning, set the appropriate flag.
    260     if (shouldWarn &&
    261         (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) {
    262       runtime->SetPendingHiddenApiWarning(true);
    263     }
    264   }
    265 
    266   return action;
    267 }
    268 
    269 // Need to instantiate this.
    270 template Action GetMemberActionImpl<ArtField>(ArtField* member,
    271                                               HiddenApiAccessFlags::ApiList api_list,
    272                                               Action action,
    273                                               AccessMethod access_method);
    274 template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
    275                                                HiddenApiAccessFlags::ApiList api_list,
    276                                                Action action,
    277                                                AccessMethod access_method);
    278 }  // namespace detail
    279 
    280 template<typename T>
    281 void NotifyHiddenApiListener(T* member) {
    282   Runtime* runtime = Runtime::Current();
    283   if (!runtime->IsAotCompiler()) {
    284     ScopedObjectAccessUnchecked soa(Thread::Current());
    285 
    286     ScopedLocalRef<jobject> consumer_object(soa.Env(),
    287         soa.Env()->GetStaticObjectField(
    288             WellKnownClasses::dalvik_system_VMRuntime,
    289             WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
    290     // If the consumer is non-null, we call back to it to let it know that we
    291     // have encountered an API that's in one of our lists.
    292     if (consumer_object != nullptr) {
    293       detail::MemberSignature member_signature(member);
    294       std::ostringstream member_signature_str;
    295       member_signature.Dump(member_signature_str);
    296 
    297       ScopedLocalRef<jobject> signature_str(
    298           soa.Env(),
    299           soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
    300 
    301       // Call through to Consumer.accept(String memberSignature);
    302       soa.Env()->CallVoidMethod(consumer_object.get(),
    303                                 WellKnownClasses::java_util_function_Consumer_accept,
    304                                 signature_str.get());
    305     }
    306   }
    307 }
    308 
    309 template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member);
    310 template void NotifyHiddenApiListener<ArtField>(ArtField* member);
    311 
    312 }  // namespace hiddenapi
    313 }  // namespace art
    314