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.
SetDexSections()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 
RegisterDexFile(const DexFile * dex_file)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 
SetRegistrationRange(const void * begin,size_t size,bool should_poison)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 
SetCurrentRanges()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 
SetDexFileRegistration(bool should_poison)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 
SetAllCodeItemRegistration(bool should_poison)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 
SetAllCodeItemStartRegistration(bool should_poison)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 
SetAllInsnsRegistration(bool should_poison)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 
SetCodeItemRegistration(const char * class_name,bool should_poison)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 
SetAllStringDataStartRegistration(bool should_poison)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 
SetAllStringDataRegistration(bool should_poison)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