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