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