staticvoid* LimitRealloc(void* old_mem, size_t bytes) {
size_t old_usable_size = LimitUsableSize(old_mem); void* new_ptr; // Need to check the size only if the allocation will increase in size. if (bytes > old_usable_size && !CheckLimit(bytes - old_usable_size)) {
warning_log("malloc_limit: realloc(%p, %zu) exceeds limit %" PRId64, old_mem, bytes,
gAllocLimit); // Free the old pointer.
LimitFree(old_mem); return nullptr;
}
#ifdefined(LIBC_STATIC) staticbool EnableLimitDispatchTable() { // This is the only valid way to modify the dispatch tables for a // static executable so no locks are necessary.
__libc_globals.mutate([](libc_globals* globals) {
atomic_store(&globals->current_dispatch_table, &__limit_dispatch);
}); returntrue;
} #else staticbool EnableLimitDispatchTable() {
pthread_mutex_lock(&gGlobalsMutateLock); // All other code that calls mutate will grab the gGlobalsMutateLock. // However, there is one case where the lock cannot be acquired, in the // signal handler that enables heapprofd. In order to avoid having two // threads calling mutate at the same time, use an atomic variable to // verify that only this function or the signal handler are calling mutate. // If this function is called at the same time as the signal handler is // being called, allow a short period for the signal handler to complete // before failing. bool enabled = false;
size_t num_tries = 200; while (true) { if (!atomic_exchange(&gGlobalsMutating, true)) {
__libc_globals.mutate([](libc_globals* globals) {
atomic_store(&globals->current_dispatch_table, &__limit_dispatch);
});
atomic_store(&gGlobalsMutating, false);
enabled = true; break;
} if (--num_tries == 0) { break;
}
usleep(1000);
}
pthread_mutex_unlock(&gGlobalsMutateLock); if (enabled) {
info_log("malloc_limit: Allocation limit enabled, max size %" PRId64 " bytes\n", gAllocLimit);
} else {
error_log("malloc_limit: Failed to enable allocation limit.");
} return enabled;
} #endif
static _Atomic bool limit_enabled; if (atomic_exchange(&limit_enabled, true)) { // The limit can only be enabled once.
error_log("malloc_limit: The allocation limit has already been set, it can only be set once."); returnfalse;
}
gAllocLimit = *reinterpret_cast<size_t*>(arg); #if __has_feature(hwaddress_sanitizer)
size_t current_allocated = __sanitizer_get_current_allocated_bytes(); #else
size_t current_allocated; auto dispatch_table = GetDefaultDispatchTable(); if (__predict_false(dispatch_table != nullptr)) {
current_allocated = dispatch_table->mallinfo().uordblks;
} else {
current_allocated = Malloc(mallinfo)().uordblks;
} #endif // This has to be set before the enable occurs since "gAllocated" is used // to compute the limit. If the enable fails, "gAllocated" is never used.
atomic_store(&gAllocated, current_allocated);
if (!EnableLimitDispatchTable()) { // Failed to enable, reset so a future enable will pass.
atomic_store(&limit_enabled, false); returnfalse;
} returntrue;
}
static size_t LimitUsableSize(constvoid* mem) { auto dispatch_table = GetDefaultDispatchTable(); if (__predict_false(dispatch_table != nullptr)) { return dispatch_table->malloc_usable_size(mem);
} return Malloc(malloc_usable_size)(mem);
}
staticstruct mallinfo LimitMallinfo() { auto dispatch_table = GetDefaultDispatchTable(); if (__predict_false(dispatch_table != nullptr)) { return dispatch_table->mallinfo();
} return Malloc(mallinfo)();
}
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.