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