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