/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "dex_file_tracking_registrar.h"
#include <deque>
#include <tuple>
#include <android-base/logging.h>
// For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for
// the ifdefs and early include.
#ifdef ART_DEX_FILE_ACCESS_TRACKING
#ifndef ART_ENABLE_ADDRESS_SANITIZER
#define ART_ENABLE_ADDRESS_SANITIZER
#endif
#endif
#include "base/memory_tool.h"
#include "class_accessor-inl.h"
#include "code_item_accessors-inl.h"
#include "dex_file-inl.h"
namespace art {
namespace dex {
namespace tracking {
// If true, poison dex files to track accesses.
static constexpr bool kDexFileAccessTracking =
#ifdef ART_DEX_FILE_ACCESS_TRACKING
true;
#else
false;
#endif
// The following are configurations of poisoning certain sections of a Dex File.
// More will be added
enum DexTrackingType {
// Poisons all of a Dex File when set.
kWholeDexTracking,
// Poisons all Code Items of a Dex File when set.
kCodeItemTracking,
// Poisons all subsections of a Code Item, except the Insns bytecode array
// section, when set for all Code Items in a Dex File.
kCodeItemNonInsnsTracking,
// Poisons all subsections of a Code Item, except the Insns bytecode array
// section, when set for all Code Items in a Dex File.
// Additionally unpoisons the entire Code Item when method is a class
// initializer.
kCodeItemNonInsnsNoClinitTracking,
// Poisons the size and offset information along with the first instruction.
// This is so that accessing multiple instructions while accessing a code item
// once will not trigger unnecessary accesses.
kCodeItemStartTracking,
// Poisons all String Data Items of a Dex Files when set.
kStringDataItemTracking,
// Poisons the first byte of the utf16_size value and the first byte of the
// data section for all String Data Items of a Dex File.
kStringDataItemStartTracking,
// Poisons based on a custom tracking system which can be specified in
// SetDexSections
kCustomTracking,
};
// Intended for local changes only.
// Represents the current configuration being run.
static constexpr DexTrackingType kCurrentTrackingSystem = kWholeDexTracking;
// Intended for local changes only.
void DexFileTrackingRegistrar::SetDexSections() {
if (kDexFileAccessTracking && dex_file_ != nullptr) {
// Logs the Dex File's location and starting address if tracking is enabled
LOG(ERROR) << "RegisterDexFile: " << dex_file_->GetLocation() + " @ " << std::hex
<< reinterpret_cast<uintptr_t>(dex_file_->Begin());
switch (kCurrentTrackingSystem) {
case kWholeDexTracking:
SetDexFileRegistration(true);
break;
case kCodeItemTracking:
SetAllCodeItemRegistration(true);
break;
case kCodeItemNonInsnsTracking:
SetAllCodeItemRegistration(true);
SetAllInsnsRegistration(false);
break;
case kCodeItemNonInsnsNoClinitTracking:
SetAllCodeItemRegistration(true);
SetAllInsnsRegistration(false);
SetCodeItemRegistration("<clinit>", false);
break;
case kCodeItemStartTracking:
SetAllCodeItemStartRegistration(true);
break;
case kStringDataItemTracking:
SetAllStringDataRegistration(true);
break;
case kStringDataItemStartTracking:
SetAllStringDataStartRegistration(true);
break;
case kCustomTracking:
// TODO: Add/remove additional calls here to (un)poison sections of
// dex_file_
break;
default:
break;
}
}
}
void RegisterDexFile(const DexFile* dex_file) {
DexFileTrackingRegistrar dex_tracking_registrar(dex_file);
dex_tracking_registrar.SetDexSections();
dex_tracking_registrar.SetCurrentRanges();
}
inline void SetRegistrationRange(const void* begin, size_t size, bool should_poison) {
if (should_poison) {
MEMORY_TOOL_MAKE_NOACCESS(begin, size);
} else {
// Note: MEMORY_TOOL_MAKE_UNDEFINED has the same functionality with Address
// Sanitizer.
// Historical note: The difference has not been tested with Valgrind.
MEMORY_TOOL_MAKE_DEFINED(begin, size);
}
}
void DexFileTrackingRegistrar::SetCurrentRanges() {
// This also empties range_values_ to avoid redundant (un)poisoning upon
// subsequent calls.
while (!range_values_.empty()) {
const std::tuple<const void*, size_t, bool>& current_range = range_values_.front();
SetRegistrationRange(std::get<0>(current_range),
std::get<1>(current_range),
std::get<2>(current_range));
range_values_.pop_front();
}
}
void DexFileTrackingRegistrar::SetDexFileRegistration(bool should_poison) {
const void* dex_file_begin = reinterpret_cast<const void*>(dex_file_->Begin());
size_t dex_file_size = dex_file_->Size();
range_values_.push_back(std::make_tuple(dex_file_begin, dex_file_size, should_poison));
}
void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) {
for (ClassAccessor accessor : dex_file_->GetClasses()) {
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
const dex::CodeItem* code_item = method.GetCodeItem();
if (code_item != nullptr) {
const void* code_item_begin = reinterpret_cast<const void*>(code_item);
size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
}
}
}
}
void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poison) {
for (ClassAccessor class_accessor : dex_file_->GetClasses()) {
for (const ClassAccessor::Method& method : class_accessor.GetMethods()) {
const dex::CodeItem* code_item = method.GetCodeItem();
if (code_item != nullptr) {
const void* code_item_begin = reinterpret_cast<const void*>(code_item);
size_t code_item_start = reinterpret_cast<size_t>(code_item);
CodeItemInstructionAccessor accessor(*dex_file_, code_item);
size_t code_item_start_end = reinterpret_cast<size_t>(accessor.Insns());
size_t code_item_start_size = code_item_start_end - code_item_start;
range_values_.push_back(std::make_tuple(code_item_begin,
code_item_start_size,
should_poison));
}
}
}
}
void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) {
for (ClassAccessor class_accessor : dex_file_->GetClasses()) {
for (const ClassAccessor::Method& method : class_accessor.GetMethods()) {
const dex::CodeItem* code_item = method.GetCodeItem();
if (code_item != nullptr) {
CodeItemInstructionAccessor accessor(*dex_file_, code_item);
const void* insns_begin = reinterpret_cast<const void*>(accessor.Insns());
// Member insns_size_in_code_units_ is in 2-byte units
size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2;
range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison));
}
}
}
}
void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, bool should_poison) {
for (ClassAccessor accessor : dex_file_->GetClasses()) {
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
const dex::MethodId& methodid_item = dex_file_->GetMethodId(method.GetIndex());
const char * methodid_name = dex_file_->GetMethodName(methodid_item);
const dex::CodeItem* code_item = method.GetCodeItem();
if (code_item != nullptr && strcmp(methodid_name, class_name) == 0) {
const void* code_item_begin = reinterpret_cast<const void*>(code_item);
size_t code_item_size = dex_file_->GetCodeItemSize(*code_item);
range_values_.push_back(std::make_tuple(code_item_begin, code_item_size, should_poison));
}
}
}
}
void DexFileTrackingRegistrar::SetAllStringDataStartRegistration(bool should_poison) {
for (size_t stringid_ctr = 0; stringid_ctr < dex_file_->NumStringIds(); ++stringid_ctr) {
const dex::StringId & string_id = dex_file_->GetStringId(StringIndex(stringid_ctr));
const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + string_id.string_data_off_);
// Data Section of String Data Item
const void* string_data_data_begin = reinterpret_cast<const void*>(dex_file_->GetStringData(string_id));
range_values_.push_back(std::make_tuple(string_data_begin, 1, should_poison));
range_values_.push_back(std::make_tuple(string_data_data_begin, 1, should_poison));
}
}
void DexFileTrackingRegistrar::SetAllStringDataRegistration(bool should_poison) {
size_t map_offset = dex_file_->GetHeader().map_off_;
auto map_list = reinterpret_cast<const dex::MapList*>(dex_file_->Begin() + map_offset);
for (size_t map_ctr = 0; map_ctr < map_list->size_; ++map_ctr) {
const dex::MapItem& map_item = map_list->list_[map_ctr];
if (map_item.type_ == DexFile::kDexTypeStringDataItem) {
const dex::MapItem& next_map_item = map_list->list_[map_ctr + 1];
const void* string_data_begin = reinterpret_cast<const void*>(dex_file_->Begin() + map_item.offset_);
size_t string_data_size = next_map_item.offset_ - map_item.offset_;
range_values_.push_back(std::make_tuple(string_data_begin, string_data_size, should_poison));
}
}
}
} // namespace tracking
} // namespace dex
} // namespace art