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