using Mode = android_mallopt_gwp_asan_options_t::Mode; using Options = gwp_asan::options::Options;
// basename() is a mess, see the manpage. Let's be explicit what handling we // want (don't touch my string!). extern"C"constchar* __gnu_basename(constchar* path);
namespace {
// ============================================================================ // Implementation of GWP-ASan malloc wrappers. // ============================================================================
void* gwp_asan_calloc(size_t n_elements, size_t elem_size) { if (__predict_false(GuardedAlloc.shouldSample())) {
size_t bytes; if (!__builtin_mul_overflow(n_elements, elem_size, &bytes)) { if (void* result = GuardedAlloc.allocate(bytes)) { return result;
}
}
} return prev_dispatch->calloc(n_elements, elem_size);
}
void* gwp_asan_realloc(void* old_mem, size_t bytes) { // GPA::pointerIsMine(p) always returns false where `p == nullptr` (and thus // malloc(bytes) is requested). We always fall back to the backing allocator, // technically missing some coverage, but reducing an extra conditional // branch. if (__predict_false(GuardedAlloc.pointerIsMine(old_mem))) { if (__predict_false(bytes == 0)) {
GuardedAlloc.deallocate(old_mem); return nullptr;
} void* new_ptr = gwp_asan_malloc(bytes); // If malloc() fails, then don't destroy the old memory. if (__predict_false(new_ptr == nullptr)) return nullptr;
bool ShouldGwpAsanSampleProcess(unsigned sample_rate) { if (!powerof2(sample_rate)) {
warning_log( "GWP-ASan process sampling rate of %u is not a power-of-two, and so modulo bias occurs.",
sample_rate);
}
// The probability (1 / SampleRate) that an allocation gets chosen to be put // into the special GWP-ASan pool. using SampleRate_t = typeof(gwp_asan::options::Options::SampleRate);
constexpr SampleRate_t kDefaultSampleRate = 2500; staticconstchar* kSampleRateSystemSysprop = "libc.debug.gwp_asan.sample_rate.system_default"; staticconstchar* kSampleRateAppSysprop = "libc.debug.gwp_asan.sample_rate.app_default"; staticconstchar* kSampleRateTargetedSyspropPrefix = "libc.debug.gwp_asan.sample_rate."; staticconstchar* kSampleRateEnvVar = "GWP_ASAN_SAMPLE_RATE";
// The probability (1 / ProcessSampling) that a process will be randomly // selected for sampling, for system apps and system processes. The process // sampling rate should always be a power of two to avoid modulo bias.
constexpr unsigned kDefaultProcessSampling = 128; staticconstchar* kProcessSamplingSystemSysprop = "libc.debug.gwp_asan.process_sampling.system_default"; staticconstchar* kProcessSamplingAppSysprop = "libc.debug.gwp_asan.process_sampling.app_default"; staticconstchar* kProcessSamplingTargetedSyspropPrefix = "libc.debug.gwp_asan.process_sampling."; staticconstchar* kProcessSamplingEnvVar = "GWP_ASAN_PROCESS_SAMPLING";
// The upper limit of simultaneous allocations supported by GWP-ASan. Any // allocations in excess of this limit will be passed to the backing allocator // and can't be sampled. This value, if unspecified, will be automatically // calculated to keep the same ratio as the default (2500 sampling : 32 allocs). // So, if you specify GWP_ASAN_SAMPLE_RATE=1250 (i.e. twice as frequent), we'll // automatically calculate that we need double the slots (64). using SimultaneousAllocations_t = typeof(gwp_asan::options::Options::MaxSimultaneousAllocations);
constexpr SimultaneousAllocations_t kDefaultMaxAllocs = 32; staticconstchar* kMaxAllocsSystemSysprop = "libc.debug.gwp_asan.max_allocs.system_default"; staticconstchar* kMaxAllocsAppSysprop = "libc.debug.gwp_asan.max_allocs.app_default"; staticconstchar* kMaxAllocsTargetedSyspropPrefix = "libc.debug.gwp_asan.max_allocs."; staticconstchar* kMaxAllocsEnvVar = "GWP_ASAN_MAX_ALLOCS";
constexpr size_t kSyspropMaxLen = 512; char program_specific_sysprop[kSyspropMaxLen] = {}; char persist_program_specific_sysprop[kSyspropMaxLen] = {}; char persist_default_sysprop[kSyspropMaxLen] = {}; constchar* sysprop_names[4] = {}; // Tests use a blank program name to specify that system properties should not // be used. Tests still continue to use the environment variable though. if (*basename != '\0') { constchar* default_sysprop = system_sysprop; if (mallopt_options.mode == Mode::APP_MANIFEST_ALWAYS) {
default_sysprop = app_sysprop;
}
async_safe_format_buffer(&program_specific_sysprop[0], kSyspropMaxLen, "%s%s",
targeted_sysprop_prefix, basename);
async_safe_format_buffer(&persist_program_specific_sysprop[0], kSyspropMaxLen, "%s%s",
kPersistPrefix, program_specific_sysprop);
async_safe_format_buffer(&persist_default_sysprop[0], kSyspropMaxLen, "%s%s", kPersistPrefix,
default_sysprop);
// In order of precedence, always take the program-specific sysprop (e.g. // '[persist.]libc.debug.gwp_asan.sample_rate.cameraserver') over the // generic sysprop (e.g. // '[persist.]libc.debug.gwp_asan.(system_default|app_default)'). In // addition, always take the non-persistent option over the persistent // option.
sysprop_names[0] = program_specific_sysprop;
sysprop_names[1] = persist_program_specific_sysprop;
sysprop_names[2] = default_sysprop;
sysprop_names[3] = persist_default_sysprop;
}
warning_log( "Invalid GWP-ASan %s: \"%s\". Using default value \"%s\" instead. Valid values are \"true\", " "\"1\", \"false\", or \"0\".",
descriptive_name, buffer, *result ? "true" : "false"); returnfalse;
}
// Initialize the GWP-ASan options structure in *options, taking into account whether someone has // asked for specific GWP-ASan settings. The order of priority is: // 1. Environment variables. // 2. Process-specific system properties. // 3. Global system properties. // If any of these overrides are found, we return true. Otherwise, use the default values, and // return false. bool GetGwpAsanOptions(Options* options, unsigned* process_sample_rate, const android_mallopt_gwp_asan_options_t& mallopt_options) {
SetDefaultGwpAsanOptions(options, process_sample_rate, mallopt_options);
if (GetGwpAsanIntegerOption(&buf, mallopt_options, kMaxAllocsSystemSysprop, kMaxAllocsAppSysprop,
kMaxAllocsTargetedSyspropPrefix, kMaxAllocsEnvVar, "maximum simultaneous allocations")) {
options->MaxSimultaneousAllocations = buf;
had_overrides = true;
} elseif (had_overrides) { // Multiply the number of slots available, such that the ratio between // sampling rate and slots is kept the same as the default. For example, a // sampling rate of 1000 is 2.5x more frequent than default, and so // requires 80 slots (32 * 2.5). float frequency_multiplier = static_cast<float>(options->SampleRate) / kDefaultSampleRate;
options->MaxSimultaneousAllocations = /* default */ kDefaultMaxAllocs / frequency_multiplier;
}
bool MaybeInitGwpAsan(libc_globals* globals, const android_mallopt_gwp_asan_options_t& mallopt_options) { if (GwpAsanInitialized) {
error_log("GWP-ASan was already initialized for this process."); returnfalse;
}
if (!ShouldGwpAsanSampleProcess(process_sample_rate)) { returnfalse;
}
// GWP-ASan is compatible with heapprofd/malloc_debug/malloc_hooks iff // GWP-ASan was installed first. If one of these other libraries was already // installed, we don't enable GWP-ASan. These libraries are normally enabled // in libc_init after GWP-ASan, but if the new process is a zygote child and // trying to initialize GWP-ASan through mallopt(), one of these libraries may // be installed. It may be possible to change this in future by modifying the // internal dispatch pointers of these libraries at this point in time, but // given that they're all debug-only, we don't really mind for now. if (GetDefaultDispatchTable() != nullptr) { // Something else is installed. returnfalse;
}
// GWP-ASan's initialization is always called in a single-threaded context, so // we can initialize lock-free. // Set GWP-ASan as the malloc dispatch table.
globals->malloc_dispatch_table = gwp_asan_dispatch;
atomic_store(&globals->default_dispatch_table, &gwp_asan_dispatch);
// If malloc_limit isn't installed, we can skip the default_dispatch_table // lookup. if (GetDispatchTable() == nullptr) {
atomic_store(&globals->current_dispatch_table, &gwp_asan_dispatch);
}
bool MaybeInitGwpAsanFromLibc(libc_globals* globals) { // Never initialize the Zygote here. A Zygote chosen for sampling would also // have all of its children sampled. Instead, the Zygote child will choose // whether it samples or not just after the Zygote forks. Note that the Zygote // changes its name after it's started, at this point it's still called // "app_process" or "app_process64". staticconstchar kAppProcessNamePrefix[] = "app_process"; constchar* progname = getprogname(); if (strncmp(progname, kAppProcessNamePrefix, sizeof(kAppProcessNamePrefix) - 1) == 0) returnfalse;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.