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