Home | History | Annotate | Download | only in dex
      1 /*
      2  * Copyright (C) 2017 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 "dex_file_tracking_registrar.h"
     18 
     19 #include <deque>
     20 #include <tuple>
     21 
     22 #include <android-base/logging.h>
     23 
     24 // For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for
     25 // the ifdefs and early include.
     26 #ifdef ART_DEX_FILE_ACCESS_TRACKING
     27 #ifndef ART_ENABLE_ADDRESS_SANITIZER
     28 #define ART_ENABLE_ADDRESS_SANITIZER
     29 #endif
     30 #endif
     31 #include "base/memory_tool.h"
     32 
     33 #include "class_accessor-inl.h"
     34 #include "code_item_accessors-inl.h"
     35 #include "dex_file-inl.h"
     36 
     37 namespace art {
     38 namespace dex {
     39 namespace tracking {
     40 
     41 // If true, poison dex files to track accesses.
     42 static constexpr bool kDexFileAccessTracking =
     43 #ifdef ART_DEX_FILE_ACCESS_TRACKING
     44     true;
     45 #else
     46     false;
     47 #endif
     48 
     49 // The following are configurations of poisoning certain sections of a Dex File.
     50 // More will be added
     51 enum DexTrackingType {
     52   // Poisons all of a Dex File when set.
     53   kWholeDexTracking,
     54   // Poisons all Code Items of a Dex File when set.
     55   kCodeItemTracking,
     56   // Poisons all subsections of a Code Item, except the Insns bytecode array
     57   // section, when set for all Code Items in a Dex File.
     58   kCodeItemNonInsnsTracking,
     59   // Poisons all subsections of a Code Item, except the Insns bytecode array
     60   // section, when set for all Code Items in a Dex File.
     61   // Additionally unpoisons the entire Code Item when method is a class
     62   // initializer.
     63   kCodeItemNonInsnsNoClinitTracking,
     64   // Poisons the size and offset information along with the first instruction.
     65   // This is so that accessing multiple instructions while accessing a code item
     66   // once will not trigger unnecessary accesses.
     67   kCodeItemStartTracking,
     68   // Poisons all String Data Items of a Dex Files when set.
     69   kStringDataItemTracking,
     70   // Poisons the first byte of the utf16_size value and the first byte of the
     71   // data section for all String Data Items of a Dex File.
     72   kStringDataItemStartTracking,
     73   // Poisons based on a custom tracking system which can be specified in
     74   // SetDexSections
     75   kCustomTracking,
     76 };
     77 
     78 // Intended for local changes only.
     79 // Represents the current configuration being run.
     80 static constexpr DexTrackingType kCurrentTrackingSystem = kWholeDexTracking;
     81 
     82 // Intended for local changes only.
     83 void DexFileTrackingRegistrar::SetDexSections() {
     84   if (kDexFileAccessTracking && dex_file_ != nullptr) {
     85     // Logs the Dex File's location and starting address if tracking is enabled
     86     LOG(ERROR) << "RegisterDexFile: " << dex_file_->GetLocation() + " @ " << std::hex
     87                << reinterpret_cast<uintptr_t>(dex_file_->Begin());
     88     switch (kCurrentTrackingSystem) {
     89       case kWholeDexTracking:
     90         SetDexFileRegistration(true);
     91         break;
     92       case kCodeItemTracking:
     93         SetAllCodeItemRegistration(true);
     94         break;
     95       case kCodeItemNonInsnsTracking:
     96         SetAllCodeItemRegistration(true);
     97         SetAllInsnsRegistration(false);
     98         break;
     99       case kCodeItemNonInsnsNoClinitTracking:
    100         SetAllCodeItemRegistration(true);
    101         SetAllInsnsRegistration(false);
    102         SetCodeItemRegistration("<clinit>", false);
    103         break;
    104       case kCodeItemStartTracking:
    105         SetAllCodeItemStartRegistration(true);
    106         break;
    107       case kStringDataItemTracking:
    108         SetAllStringDataRegistration(true);
    109         break;
    110       case kStringDataItemStartTracking:
    111         SetAllStringDataStartRegistration(true);
    112         break;
    113       case kCustomTracking:
    114         // TODO: Add/remove additional calls here to (un)poison sections of
    115         // dex_file_
    116         break;
    117       default:
    118         break;
    119     }
    120   }
    121 }
    122 
    123 void RegisterDexFile(const DexFile* dex_file) {
    124   DexFileTrackingRegistrar dex_tracking_registrar(dex_file);
    125   dex_tracking_registrar.SetDexSections();
    126   dex_tracking_registrar.SetCurrentRanges();
    127 }
    128 
    129 inline void SetRegistrationRange(const void* begin, size_t size, bool should_poison) {
    130   if (should_poison) {
    131     MEMORY_TOOL_MAKE_NOACCESS(begin, size);
    132   } else {
    133     // Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address
    134     // Sanitizer.
    135     // Historical note: The difference has not been tested with Valgrind.
    136     MEMORY_TOOL_MAKE_DEFINED(begin, size);
    137   }
    138 }
    139 
    140 void DexFileTrackingRegistrar::SetCurrentRanges() {
    141   // This also empties range_values_ to avoid redundant (un)poisoning upon
    142   // subsequent calls.
    143   while (!range_values_.empty()) {
    144     const std::tuple<const void*, size_t, bool>& current_range = range_values_.front();
    145     SetRegistrationRange(std::get<0>(current_range),
    146                          std::get<1>(current_range),
    147                          std::get<2>(current_range));
    148     range_values_.pop_front();
    149   }
    150 }
    151 
    152 void DexFileTrackingRegistrar::SetDexFileRegistration(bool should_poison) {
    153   const void* dex_file_begin = reinterpret_cast<const void*>(dex_file_->Begin());
    154   size_t dex_file_size = dex_file_->Size();
    155   range_values_.push_back(std::make_tuple(dex_file_begin, dex_file_size, should_poison));
    156 }
    157 
    158 void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) {
    159   for (ClassAccessor accessor : dex_file_->GetClasses()) {
    160     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
    161       const dex::CodeItem* code_item = method.GetCodeItem();
    162       if (code_item != nullptr) {
    163         const void* code_item_begin = reinterpret_cast<const void*>(code_item);
    164         size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
    165         range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
    166       }
    167     }
    168   }
    169 }
    170 
    171 void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poison) {
    172   for (ClassAccessor class_accessor : dex_file_->GetClasses()) {
    173     for (const ClassAccessor::Method& method : class_accessor.GetMethods()) {
    174       const dex::CodeItem* code_item = method.GetCodeItem();
    175       if (code_item != nullptr) {
    176         const void* code_item_begin = reinterpret_cast<const void*>(code_item);
    177         size_t code_item_start = reinterpret_cast<size_t>(code_item);
    178         CodeItemInstructionAccessor accessor(*dex_file_, code_item);
    179         size_t code_item_start_end = reinterpret_cast<size_t>(accessor.Insns());
    180         size_t code_item_start_size = code_item_start_end - code_item_start;
    181         range_values_.push_back(std::make_tuple(code_item_begin,
    182                                                 code_item_start_size,
    183                                                 should_poison));
    184       }
    185     }
    186   }
    187 }
    188 
    189 void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) {
    190   for (ClassAccessor class_accessor : dex_file_->GetClasses()) {
    191     for (const ClassAccessor::Method& method : class_accessor.GetMethods()) {
    192       const dex::CodeItem* code_item = method.GetCodeItem();
    193       if (code_item != nullptr) {
    194         CodeItemInstructionAccessor accessor(*dex_file_, code_item);
    195         const void* insns_begin = reinterpret_cast<const void*>(accessor.Insns());
    196         // Member insns_size_in_code_units_ is in 2-byte units
    197         size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2;
    198         range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison));
    199       }
    200     }
    201   }
    202 }
    203 
    204 void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, bool should_poison) {
    205   for (ClassAccessor accessor : dex_file_->GetClasses()) {
    206     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
    207       const dex::MethodId& methodid_item = dex_file_->GetMethodId(method.GetIndex());
    208       const char * methodid_name = dex_file_->GetMethodName(methodid_item);
    209       const dex::CodeItem* code_item = method.GetCodeItem();
    210       if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) {
    211         const void* code_item_begin = reinterpret_cast<const void*>(code_item);
    212         size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
    213         range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
    214       }
    215     }
    216   }
    217 }
    218 
    219 void DexFileTrackingRegistrar::SetAllStringDataStartRegistration(bool should_poison) {
    220   for (size_t stringid_ctr = 0; stringid_ctr < dex_file_->NumStringIds(); ++stringid_ctr) {
    221     const dex::StringId & string_id = dex_file_->GetStringId(StringIndex(stringid_ctr));
    222     const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + string_id.string_data_off_);
    223     // Data Section of String Data Item
    224     const void* string_data_data_begin = reinterpret_cast<const void*>(dex_file_->GetStringData(string_id));
    225     range_values_.push_back(std::make_tuple(string_data_begin, 1, should_poison));
    226     range_values_.push_back(std::make_tuple(string_data_data_begin, 1, should_poison));
    227   }
    228 }
    229 
    230 void DexFileTrackingRegistrar::SetAllStringDataRegistration(bool should_poison) {
    231   size_t map_offset = dex_file_->GetHeader().map_off_;
    232   auto map_list = reinterpret_cast<const dex::MapList*>(dex_file_->Begin() + map_offset);
    233   for (size_t map_ctr = 0; map_ctr < map_list->size_; ++map_ctr) {
    234     const dex::MapItem& map_item = map_list->list_[map_ctr];
    235     if (map_item.type_ == DexFile::kDexTypeStringDataItem) {
    236       const dex::MapItem& next_map_item = map_list->list_[map_ctr + 1];
    237       const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + map_item.offset_);
    238       size_t string_data_size = next_map_item.offset_ - map_item.offset_;
    239       range_values_.push_back(std::make_tuple(string_data_begin, string_data_size, should_poison));
    240     }
    241   }
    242 }
    243 
    244 }  // namespace tracking
    245 }  // namespace dex
    246 }  // namespace art
    247