Merge "Put all allocation functions into dispatch table."

This commit is contained in:
Christopher Ferris 2014-06-13 00:40:04 +00:00 committed by Gerrit Code Review
commit 29ddd78c21
7 changed files with 698 additions and 451 deletions

View file

@ -326,14 +326,19 @@ static inline void add_to_backlog(hdr_t* hdr) {
}
}
extern "C" void* chk_malloc(size_t size) {
extern "C" void* chk_malloc(size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
hdr_t* hdr = static_cast<hdr_t*>(Malloc(malloc)(sizeof(hdr_t) + size + sizeof(ftr_t)));
size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t);
if (size < bytes) { // Overflow
errno = ENOMEM;
return NULL;
}
hdr_t* hdr = static_cast<hdr_t*>(Malloc(malloc)(size));
if (hdr) {
hdr->base = hdr;
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
add(hdr, size);
add(hdr, bytes);
return user(hdr);
}
return NULL;
@ -411,15 +416,15 @@ extern "C" void chk_free(void* ptr) {
}
}
extern "C" void* chk_realloc(void* ptr, size_t size) {
extern "C" void* chk_realloc(void* ptr, size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
if (!ptr) {
return chk_malloc(size);
return chk_malloc(bytes);
}
#ifdef REALLOC_ZERO_BYTES_FREE
if (!size) {
if (!bytes) {
chk_free(ptr);
return NULL;
}
@ -432,7 +437,7 @@ extern "C" void* chk_realloc(void* ptr, size_t size) {
int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
if (hdr->tag == BACKLOG_TAG) {
log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n",
user(hdr), size, hdr->size);
user(hdr), bytes, hdr->size);
log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
user(hdr), hdr->size);
log_backtrace(hdr->bt, hdr->bt_depth);
@ -451,47 +456,54 @@ extern "C" void* chk_realloc(void* ptr, size_t size) {
del_from_backlog(hdr);
} else {
log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
user(hdr), size);
user(hdr), bytes);
log_backtrace(bt, depth);
// just get a whole new allocation and leak the old one
return Malloc(realloc)(0, size);
// return realloc(user(hdr), size); // assuming it was allocated externally
return Malloc(realloc)(0, bytes);
// return realloc(user(hdr), bytes); // assuming it was allocated externally
}
}
size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t);
if (size < bytes) { // Overflow
errno = ENOMEM;
return NULL;
}
if (hdr->base != hdr) {
// An allocation from memalign, so create another allocation and
// copy the data out.
void* newMem = Malloc(malloc)(sizeof(hdr_t) + size + sizeof(ftr_t));
if (newMem) {
memcpy(newMem, hdr, sizeof(hdr_t) + hdr->size);
Malloc(free)(hdr->base);
hdr = static_cast<hdr_t*>(newMem);
} else {
Malloc(free)(hdr->base);
hdr = NULL;
void* newMem = Malloc(malloc)(size);
if (newMem == NULL) {
return NULL;
}
memcpy(newMem, hdr, sizeof(hdr_t) + hdr->size);
Malloc(free)(hdr->base);
hdr = static_cast<hdr_t*>(newMem);
} else {
hdr = static_cast<hdr_t*>(Malloc(realloc)(hdr, sizeof(hdr_t) + size + sizeof(ftr_t)));
hdr = static_cast<hdr_t*>(Malloc(realloc)(hdr, size));
}
if (hdr) {
hdr->base = hdr;
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
add(hdr, size);
add(hdr, bytes);
return user(hdr);
}
return NULL;
}
extern "C" void* chk_calloc(int nmemb, size_t size) {
extern "C" void* chk_calloc(size_t nmemb, size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
size_t total_size = nmemb * size;
hdr_t* hdr = static_cast<hdr_t*>(Malloc(calloc)(1, sizeof(hdr_t) + total_size + sizeof(ftr_t)));
size_t total_bytes = nmemb * bytes;
size_t size = sizeof(hdr_t) + total_bytes + sizeof(ftr_t);
if (size < total_bytes || (nmemb && SIZE_MAX / nmemb < bytes)) { // Overflow
errno = ENOMEM;
return NULL;
}
hdr_t* hdr = static_cast<hdr_t*>(Malloc(calloc)(1, size));
if (hdr) {
hdr->base = hdr;
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
add(hdr, total_size);
add(hdr, total_bytes);
return user(hdr);
}
return NULL;
@ -509,6 +521,33 @@ extern "C" size_t chk_malloc_usable_size(const void* ptr) {
return hdr->size;
}
extern "C" struct mallinfo chk_mallinfo() {
return Malloc(mallinfo)();
}
extern "C" int chk_posix_memalign(void** memptr, size_t alignment, size_t size) {
if ((alignment & (alignment - 1)) != 0) {
return EINVAL;
}
int saved_errno = errno;
*memptr = chk_memalign(alignment, size);
errno = saved_errno;
return (*memptr != NULL) ? 0 : ENOMEM;
}
extern "C" void* chk_pvalloc(size_t bytes) {
size_t pagesize = sysconf(_SC_PAGESIZE);
size_t size = (bytes + pagesize - 1) & ~(pagesize - 1);
if (size < bytes) { // Overflow
return NULL;
}
return chk_memalign(pagesize, size);
}
extern "C" void* chk_valloc(size_t size) {
return chk_memalign(sysconf(_SC_PAGESIZE), size);
}
static void ReportMemoryLeaks() {
// Use /proc/self/exe link to obtain the program name for logging
// purposes. If it's not available, we set it to "<unknown>".

View file

@ -26,19 +26,17 @@
* SUCH DAMAGE.
*/
/*
* Contains definition of structures, global variables, and implementation of
* routines that are used by malloc leak detection code and other components in
* the system. The trick is that some components expect these data and
* routines to be defined / implemented in libc.so library, regardless
* whether or not MALLOC_LEAK_CHECK macro is defined. To make things even
* more tricky, malloc leak detection code, implemented in
* libc_malloc_debug.so also requires access to these variables and routines
* (to fill allocation entry hash table, for example). So, all relevant
* variables and routines are defined / implemented here and exported
* to all, leak detection code and other components via dynamic (libc.so),
* or static (libc.a) linking.
*/
// Contains definition of structures, global variables, and implementation of
// routines that are used by malloc leak detection code and other components in
// the system. The trick is that some components expect these data and
// routines to be defined / implemented in libc.so library, regardless
// whether or not MALLOC_LEAK_CHECK macro is defined. To make things even
// more tricky, malloc leak detection code, implemented in
// libc_malloc_debug.so also requires access to these variables and routines
// (to fill allocation entry hash table, for example). So, all relevant
// variables and routines are defined / implemented here and exported
// to all, leak detection code and other components via dynamic (libc.so),
// or static (libc.a) linking.
#include "malloc_debug_common.h"
@ -56,7 +54,16 @@ static HashTable g_hash_table;
// Support for malloc debugging.
// Table for dispatching malloc calls, initialized with default dispatchers.
static const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) = {
Malloc(malloc), Malloc(free), Malloc(calloc), Malloc(realloc), Malloc(memalign), Malloc(malloc_usable_size),
Malloc(calloc),
Malloc(free),
Malloc(mallinfo),
Malloc(malloc),
Malloc(malloc_usable_size),
Malloc(memalign),
Malloc(posix_memalign),
Malloc(pvalloc),
Malloc(realloc),
Malloc(valloc),
};
// Selector of dispatch table to use for dispatching malloc calls.
@ -97,176 +104,174 @@ static int g_malloc_debug_level = 0;
// =============================================================================
static int hash_entry_compare(const void* arg1, const void* arg2) {
int result;
int result;
const HashEntry* e1 = *static_cast<HashEntry* const*>(arg1);
const HashEntry* e2 = *static_cast<HashEntry* const*>(arg2);
const HashEntry* e1 = *static_cast<HashEntry* const*>(arg1);
const HashEntry* e2 = *static_cast<HashEntry* const*>(arg2);
// if one or both arg pointers are null, deal gracefully
if (e1 == NULL) {
result = (e2 == NULL) ? 0 : 1;
} else if (e2 == NULL) {
result = -1;
// if one or both arg pointers are null, deal gracefully
if (e1 == NULL) {
result = (e2 == NULL) ? 0 : 1;
} else if (e2 == NULL) {
result = -1;
} else {
size_t nbAlloc1 = e1->allocations;
size_t nbAlloc2 = e2->allocations;
size_t size1 = e1->size & ~SIZE_FLAG_MASK;
size_t size2 = e2->size & ~SIZE_FLAG_MASK;
size_t alloc1 = nbAlloc1 * size1;
size_t alloc2 = nbAlloc2 * size2;
// sort in descending order by:
// 1) total size
// 2) number of allocations
//
// This is used for sorting, not determination of equality, so we don't
// need to compare the bit flags.
if (alloc1 > alloc2) {
result = -1;
} else if (alloc1 < alloc2) {
result = 1;
} else {
size_t nbAlloc1 = e1->allocations;
size_t nbAlloc2 = e2->allocations;
size_t size1 = e1->size & ~SIZE_FLAG_MASK;
size_t size2 = e2->size & ~SIZE_FLAG_MASK;
size_t alloc1 = nbAlloc1 * size1;
size_t alloc2 = nbAlloc2 * size2;
// sort in descending order by:
// 1) total size
// 2) number of allocations
//
// This is used for sorting, not determination of equality, so we don't
// need to compare the bit flags.
if (alloc1 > alloc2) {
result = -1;
} else if (alloc1 < alloc2) {
result = 1;
} else {
if (nbAlloc1 > nbAlloc2) {
result = -1;
} else if (nbAlloc1 < nbAlloc2) {
result = 1;
} else {
result = 0;
}
}
if (nbAlloc1 > nbAlloc2) {
result = -1;
} else if (nbAlloc1 < nbAlloc2) {
result = 1;
} else {
result = 0;
}
}
return result;
}
return result;
}
/*
* Retrieve native heap information.
*
* "*info" is set to a buffer we allocate
* "*overallSize" is set to the size of the "info" buffer
* "*infoSize" is set to the size of a single entry
* "*totalMemory" is set to the sum of all allocations we're tracking; does
* not include heap overhead
* "*backtraceSize" is set to the maximum number of entries in the back trace
*/
// Retrieve native heap information.
//
// "*info" is set to a buffer we allocate
// "*overallSize" is set to the size of the "info" buffer
// "*infoSize" is set to the size of a single entry
// "*totalMemory" is set to the sum of all allocations we're tracking; does
// not include heap overhead
// "*backtraceSize" is set to the maximum number of entries in the back trace
// =============================================================================
// Exported for use by ddms.
// =============================================================================
extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize) {
// don't do anything if we have invalid arguments
if (info == NULL || overallSize == NULL || infoSize == NULL ||
totalMemory == NULL || backtraceSize == NULL) {
return;
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize) {
// Don't do anything if we have invalid arguments.
if (info == NULL || overallSize == NULL || infoSize == NULL ||
totalMemory == NULL || backtraceSize == NULL) {
return;
}
*totalMemory = 0;
ScopedPthreadMutexLocker locker(&g_hash_table.lock);
if (g_hash_table.count == 0) {
*info = NULL;
*overallSize = 0;
*infoSize = 0;
*backtraceSize = 0;
return;
}
HashEntry** list = static_cast<HashEntry**>(Malloc(malloc)(sizeof(void*) * g_hash_table.count));
// Get the entries into an array to be sorted.
size_t index = 0;
for (size_t i = 0 ; i < HASHTABLE_SIZE ; ++i) {
HashEntry* entry = g_hash_table.slots[i];
while (entry != NULL) {
list[index] = entry;
*totalMemory = *totalMemory + ((entry->size & ~SIZE_FLAG_MASK) * entry->allocations);
index++;
entry = entry->next;
}
*totalMemory = 0;
}
ScopedPthreadMutexLocker locker(&g_hash_table.lock);
if (g_hash_table.count == 0) {
*info = NULL;
*overallSize = 0;
*infoSize = 0;
*backtraceSize = 0;
return;
}
HashEntry** list = static_cast<HashEntry**>(Malloc(malloc)(sizeof(void*) * g_hash_table.count));
// get the entries into an array to be sorted
int index = 0;
for (size_t i = 0 ; i < HASHTABLE_SIZE ; ++i) {
HashEntry* entry = g_hash_table.slots[i];
while (entry != NULL) {
list[index] = entry;
*totalMemory = *totalMemory +
((entry->size & ~SIZE_FLAG_MASK) * entry->allocations);
index++;
entry = entry->next;
}
}
// XXX: the protocol doesn't allow variable size for the stack trace (yet)
*infoSize = (sizeof(size_t) * 2) + (sizeof(uintptr_t) * BACKTRACE_SIZE);
*overallSize = *infoSize * g_hash_table.count;
*backtraceSize = BACKTRACE_SIZE;
// now get a byte array big enough for this
*info = static_cast<uint8_t*>(Malloc(malloc)(*overallSize));
if (*info == NULL) {
*overallSize = 0;
Malloc(free)(list);
return;
}
qsort(list, g_hash_table.count, sizeof(void*), hash_entry_compare);
uint8_t* head = *info;
const int count = g_hash_table.count;
for (int i = 0 ; i < count ; ++i) {
HashEntry* entry = list[i];
size_t entrySize = (sizeof(size_t) * 2) + (sizeof(uintptr_t) * entry->numEntries);
if (entrySize < *infoSize) {
/* we're writing less than a full entry, clear out the rest */
memset(head + entrySize, 0, *infoSize - entrySize);
} else {
/* make sure the amount we're copying doesn't exceed the limit */
entrySize = *infoSize;
}
memcpy(head, &(entry->size), entrySize);
head += *infoSize;
}
// XXX: the protocol doesn't allow variable size for the stack trace (yet)
*infoSize = (sizeof(size_t) * 2) + (sizeof(uintptr_t) * BACKTRACE_SIZE);
*overallSize = *infoSize * g_hash_table.count;
*backtraceSize = BACKTRACE_SIZE;
// now get a byte array big enough for this
*info = static_cast<uint8_t*>(Malloc(malloc)(*overallSize));
if (*info == NULL) {
*overallSize = 0;
Malloc(free)(list);
return;
}
qsort(list, g_hash_table.count, sizeof(void*), hash_entry_compare);
uint8_t* head = *info;
const size_t count = g_hash_table.count;
for (size_t i = 0 ; i < count ; ++i) {
HashEntry* entry = list[i];
size_t entrySize = (sizeof(size_t) * 2) + (sizeof(uintptr_t) * entry->numEntries);
if (entrySize < *infoSize) {
// We're writing less than a full entry, clear out the rest.
memset(head + entrySize, 0, *infoSize - entrySize);
} else {
// Make sure the amount we're copying doesn't exceed the limit.
entrySize = *infoSize;
}
memcpy(head, &(entry->size), entrySize);
head += *infoSize;
}
Malloc(free)(list);
}
// Exported for use by ddms.
extern "C" void free_malloc_leak_info(uint8_t* info) {
Malloc(free)(info);
Malloc(free)(info);
}
extern "C" struct mallinfo mallinfo() {
return Malloc(mallinfo)();
}
extern "C" void* valloc(size_t bytes) {
return Malloc(valloc)(bytes);
}
extern "C" void* pvalloc(size_t bytes) {
return Malloc(pvalloc)(bytes);
}
extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
return Malloc(posix_memalign)(memptr, alignment, size);
}
extern "C" void* malloc(size_t bytes) {
return __libc_malloc_dispatch->malloc(bytes);
// =============================================================================
// Allocation functions
// =============================================================================
extern "C" void* calloc(size_t n_elements, size_t elem_size) {
return __libc_malloc_dispatch->calloc(n_elements, elem_size);
}
extern "C" void free(void* mem) {
__libc_malloc_dispatch->free(mem);
__libc_malloc_dispatch->free(mem);
}
extern "C" void* calloc(size_t n_elements, size_t elem_size) {
return __libc_malloc_dispatch->calloc(n_elements, elem_size);
extern "C" struct mallinfo mallinfo() {
return __libc_malloc_dispatch->mallinfo();
}
extern "C" void* realloc(void* oldMem, size_t bytes) {
return __libc_malloc_dispatch->realloc(oldMem, bytes);
}
extern "C" void* memalign(size_t alignment, size_t bytes) {
return __libc_malloc_dispatch->memalign(alignment, bytes);
extern "C" void* malloc(size_t bytes) {
return __libc_malloc_dispatch->malloc(bytes);
}
extern "C" size_t malloc_usable_size(const void* mem) {
return __libc_malloc_dispatch->malloc_usable_size(mem);
return __libc_malloc_dispatch->malloc_usable_size(mem);
}
/* We implement malloc debugging only in libc.so, so code below
* must be excluded if we compile this file for static libc.a
*/
extern "C" void* memalign(size_t alignment, size_t bytes) {
return __libc_malloc_dispatch->memalign(alignment, bytes);
}
extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
return __libc_malloc_dispatch->posix_memalign(memptr, alignment, size);
}
extern "C" void* pvalloc(size_t bytes) {
return __libc_malloc_dispatch->pvalloc(bytes);
}
extern "C" void* realloc(void* oldMem, size_t bytes) {
return __libc_malloc_dispatch->realloc(oldMem, bytes);
}
extern "C" void* valloc(size_t bytes) {
return __libc_malloc_dispatch->valloc(bytes);
}
// We implement malloc debugging only in libc.so, so the code below
// must be excluded if we compile this file for static libc.a
#ifndef LIBC_STATIC
#include <sys/system_properties.h>
#include <dlfcn.h>
@ -275,227 +280,222 @@ extern "C" size_t malloc_usable_size(const void* mem) {
template<typename FunctionType>
static void InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
char symbol[128];
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
*func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
if (*func == NULL) {
error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
}
char symbol[128];
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
*func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
if (*func == NULL) {
error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
}
}
static void InitMalloc(void* malloc_impl_handler, MallocDebug* table, const char* prefix) {
__libc_format_log(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n",
getprogname(), g_malloc_debug_level, prefix);
__libc_format_log(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n",
getprogname(), g_malloc_debug_level, prefix);
InitMallocFunction<MallocDebugMalloc>(malloc_impl_handler, &table->malloc, prefix, "malloc");
InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free");
InitMallocFunction<MallocDebugCalloc>(malloc_impl_handler, &table->calloc, prefix, "calloc");
InitMallocFunction<MallocDebugRealloc>(malloc_impl_handler, &table->realloc, prefix, "realloc");
InitMallocFunction<MallocDebugMemalign>(malloc_impl_handler, &table->memalign, prefix, "memalign");
InitMallocFunction<MallocDebugMallocUsableSize>(malloc_impl_handler, &table->malloc_usable_size, prefix, "malloc_usable_size");
InitMallocFunction<MallocDebugCalloc>(malloc_impl_handler, &table->calloc, prefix, "calloc");
InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free");
InitMallocFunction<MallocDebugMallinfo>(malloc_impl_handler, &table->mallinfo, prefix, "mallinfo");
InitMallocFunction<MallocDebugMalloc>(malloc_impl_handler, &table->malloc, prefix, "malloc");
InitMallocFunction<MallocDebugMallocUsableSize>(malloc_impl_handler, &table->malloc_usable_size, prefix, "malloc_usable_size");
InitMallocFunction<MallocDebugMemalign>(malloc_impl_handler, &table->memalign, prefix, "memalign");
InitMallocFunction<MallocDebugPosixMemalign>(malloc_impl_handler, &table->posix_memalign, prefix, "posix_memalign");
InitMallocFunction<MallocDebugPvalloc>(malloc_impl_handler, &table->pvalloc, prefix, "pvalloc");
InitMallocFunction<MallocDebugRealloc>(malloc_impl_handler, &table->realloc, prefix, "realloc");
InitMallocFunction<MallocDebugValloc>(malloc_impl_handler, &table->valloc, prefix, "valloc");
}
/* Initializes memory allocation framework once per process. */
// Initializes memory allocation framework once per process.
static void malloc_init_impl() {
const char* so_name = NULL;
MallocDebugInit malloc_debug_initialize = NULL;
unsigned int qemu_running = 0;
unsigned int memcheck_enabled = 0;
char env[PROP_VALUE_MAX];
char memcheck_tracing[PROP_VALUE_MAX];
char debug_program[PROP_VALUE_MAX];
const char* so_name = NULL;
MallocDebugInit malloc_debug_initialize = NULL;
unsigned int qemu_running = 0;
unsigned int memcheck_enabled = 0;
char env[PROP_VALUE_MAX];
char memcheck_tracing[PROP_VALUE_MAX];
char debug_program[PROP_VALUE_MAX];
/* Get custom malloc debug level. Note that emulator started with
* memory checking option will have priority over debug level set in
* libc.debug.malloc system property. */
if (__system_property_get("ro.kernel.qemu", env) && atoi(env)) {
qemu_running = 1;
if (__system_property_get("ro.kernel.memcheck", memcheck_tracing)) {
if (memcheck_tracing[0] != '0') {
// Emulator has started with memory tracing enabled. Enforce it.
g_malloc_debug_level = 20;
memcheck_enabled = 1;
}
}
// Get custom malloc debug level. Note that emulator started with
// memory checking option will have priority over debug level set in
// libc.debug.malloc system property.
if (__system_property_get("ro.kernel.qemu", env) && atoi(env)) {
qemu_running = 1;
if (__system_property_get("ro.kernel.memcheck", memcheck_tracing)) {
if (memcheck_tracing[0] != '0') {
// Emulator has started with memory tracing enabled. Enforce it.
g_malloc_debug_level = 20;
memcheck_enabled = 1;
}
}
}
/* If debug level has not been set by memcheck option in the emulator,
* lets grab it from libc.debug.malloc system property. */
if (g_malloc_debug_level == 0 && __system_property_get("libc.debug.malloc", env)) {
g_malloc_debug_level = atoi(env);
// If debug level has not been set by memcheck option in the emulator,
// lets grab it from libc.debug.malloc system property.
if (g_malloc_debug_level == 0 && __system_property_get("libc.debug.malloc", env)) {
g_malloc_debug_level = atoi(env);
}
// Debug level 0 means that we should use default allocation routines.
if (g_malloc_debug_level == 0) {
return;
}
// If libc.debug.malloc.program is set and is not a substring of progname,
// then exit.
if (__system_property_get("libc.debug.malloc.program", debug_program)) {
if (!strstr(getprogname(), debug_program)) {
return;
}
}
/* Debug level 0 means that we should use default allocation routines. */
if (g_malloc_debug_level == 0) {
return;
// mksh is way too leaky. http://b/7291287.
if (g_malloc_debug_level >= 10) {
if (strcmp(getprogname(), "sh") == 0 || strcmp(getprogname(), "/system/bin/sh") == 0) {
return;
}
}
/* If libc.debug.malloc.program is set and is not a substring of progname,
* then exit.
*/
if (__system_property_get("libc.debug.malloc.program", debug_program)) {
if (!strstr(getprogname(), debug_program)) {
return;
}
}
// mksh is way too leaky. http://b/7291287.
if (g_malloc_debug_level >= 10) {
if (strcmp(getprogname(), "sh") == 0 || strcmp(getprogname(), "/system/bin/sh") == 0) {
return;
}
}
// Choose the appropriate .so for the requested debug level.
switch (g_malloc_debug_level) {
case 1:
case 5:
case 10:
so_name = "libc_malloc_debug_leak.so";
break;
case 20:
// Quick check: debug level 20 can only be handled in emulator.
if (!qemu_running) {
error_log("%s: Debug level %d can only be set in emulator\n",
getprogname(), g_malloc_debug_level);
return;
}
// Make sure that memory checking has been enabled in emulator.
if (!memcheck_enabled) {
error_log("%s: Memory checking is not enabled in the emulator\n",
getprogname());
return;
}
so_name = "libc_malloc_debug_qemu.so";
break;
default:
error_log("%s: Debug level %d is unknown\n", getprogname(), g_malloc_debug_level);
return;
}
// Load .so that implements the required malloc debugging functionality.
void* malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
if (malloc_impl_handle == NULL) {
error_log("%s: Missing module %s required for malloc debug level %d: %s",
getprogname(), so_name, g_malloc_debug_level, dlerror());
return;
}
// Initialize malloc debugging in the loaded module.
malloc_debug_initialize = reinterpret_cast<MallocDebugInit>(dlsym(malloc_impl_handle,
"malloc_debug_initialize"));
if (malloc_debug_initialize == NULL) {
error_log("%s: Initialization routine is not found in %s\n",
getprogname(), so_name);
dlclose(malloc_impl_handle);
return;
}
if (malloc_debug_initialize(&g_hash_table) == -1) {
dlclose(malloc_impl_handle);
return;
}
if (g_malloc_debug_level == 20) {
// For memory checker we need to do extra initialization.
typedef int (*MemCheckInit)(int, const char*);
MemCheckInit memcheck_initialize =
reinterpret_cast<MemCheckInit>(dlsym(malloc_impl_handle,
"memcheck_initialize"));
if (memcheck_initialize == NULL) {
error_log("%s: memcheck_initialize routine is not found in %s\n",
getprogname(), so_name);
dlclose(malloc_impl_handle);
return;
}
if (memcheck_initialize(MALLOC_ALIGNMENT, memcheck_tracing)) {
dlclose(malloc_impl_handle);
return;
}
}
// Initialize malloc dispatch table with appropriate routines.
static MallocDebug malloc_dispatch_table __attribute__((aligned(32))) = {
Malloc(malloc),
Malloc(free),
Malloc(calloc),
Malloc(realloc),
Malloc(memalign),
Malloc(malloc_usable_size)
};
switch (g_malloc_debug_level) {
case 1:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "leak");
break;
case 5:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "fill");
break;
case 10:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "chk");
break;
case 20:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "qemu_instrumented");
break;
default:
break;
}
// Make sure dispatch table is initialized
if ((malloc_dispatch_table.malloc == NULL) ||
(malloc_dispatch_table.free == NULL) ||
(malloc_dispatch_table.calloc == NULL) ||
(malloc_dispatch_table.realloc == NULL) ||
(malloc_dispatch_table.memalign == NULL) ||
(malloc_dispatch_table.malloc_usable_size == NULL)) {
error_log("%s: some symbols for libc.debug.malloc level %d were not found (see above)",
// Choose the appropriate .so for the requested debug level.
switch (g_malloc_debug_level) {
case 1:
case 5:
case 10:
so_name = "libc_malloc_debug_leak.so";
break;
case 20:
// Quick check: debug level 20 can only be handled in emulator.
if (!qemu_running) {
error_log("%s: Debug level %d can only be set in emulator\n",
getprogname(), g_malloc_debug_level);
dlclose(malloc_impl_handle);
} else {
__libc_malloc_dispatch = &malloc_dispatch_table;
libc_malloc_impl_handle = malloc_impl_handle;
return;
}
// Make sure that memory checking has been enabled in emulator.
if (!memcheck_enabled) {
error_log("%s: Memory checking is not enabled in the emulator\n", getprogname());
return;
}
so_name = "libc_malloc_debug_qemu.so";
break;
default:
error_log("%s: Debug level %d is unknown\n", getprogname(), g_malloc_debug_level);
return;
}
// Load .so that implements the required malloc debugging functionality.
void* malloc_impl_handle = dlopen(so_name, RTLD_LAZY);
if (malloc_impl_handle == NULL) {
error_log("%s: Missing module %s required for malloc debug level %d: %s",
getprogname(), so_name, g_malloc_debug_level, dlerror());
return;
}
// Initialize malloc debugging in the loaded module.
malloc_debug_initialize = reinterpret_cast<MallocDebugInit>(dlsym(malloc_impl_handle,
"malloc_debug_initialize"));
if (malloc_debug_initialize == NULL) {
error_log("%s: Initialization routine is not found in %s\n", getprogname(), so_name);
dlclose(malloc_impl_handle);
return;
}
if (malloc_debug_initialize(&g_hash_table) == -1) {
dlclose(malloc_impl_handle);
return;
}
if (g_malloc_debug_level == 20) {
// For memory checker we need to do extra initialization.
typedef int (*MemCheckInit)(int, const char*);
MemCheckInit memcheck_initialize =
reinterpret_cast<MemCheckInit>(dlsym(malloc_impl_handle, "memcheck_initialize"));
if (memcheck_initialize == NULL) {
error_log("%s: memcheck_initialize routine is not found in %s\n",
getprogname(), so_name);
dlclose(malloc_impl_handle);
return;
}
if (memcheck_initialize(MALLOC_ALIGNMENT, memcheck_tracing)) {
dlclose(malloc_impl_handle);
return;
}
}
// No need to init the dispatch table because we can only get
// here if debug level is 1, 5, 10, or 20.
static MallocDebug malloc_dispatch_table __attribute__((aligned(32)));
switch (g_malloc_debug_level) {
case 1:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "leak");
break;
case 5:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "fill");
break;
case 10:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "chk");
break;
case 20:
InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "qemu_instrumented");
break;
default:
break;
}
// Make sure dispatch table is initialized
if ((malloc_dispatch_table.calloc == NULL) ||
(malloc_dispatch_table.free == NULL) ||
(malloc_dispatch_table.mallinfo == NULL) ||
(malloc_dispatch_table.malloc == NULL) ||
(malloc_dispatch_table.malloc_usable_size == NULL) ||
(malloc_dispatch_table.memalign == NULL) ||
(malloc_dispatch_table.posix_memalign == NULL) ||
(malloc_dispatch_table.pvalloc == NULL) ||
(malloc_dispatch_table.realloc == NULL) ||
(malloc_dispatch_table.valloc == NULL)) {
error_log("%s: some symbols for libc.debug.malloc level %d were not found (see above)",
getprogname(), g_malloc_debug_level);
dlclose(malloc_impl_handle);
} else {
__libc_malloc_dispatch = &malloc_dispatch_table;
libc_malloc_impl_handle = malloc_impl_handle;
}
}
static void malloc_fini_impl() {
// Our BSD stdio implementation doesn't close the standard streams, it only flushes them.
// And it doesn't do that until its atexit handler is run, and we run first!
// It's great that other unclosed FILE*s show up as malloc leaks, but we need to manually
// clean up the standard streams ourselves.
fclose(stdin);
fclose(stdout);
fclose(stderr);
// Our BSD stdio implementation doesn't close the standard streams, it only flushes them.
// And it doesn't do that until its atexit handler is run, and we run first!
// It's great that other unclosed FILE*s show up as malloc leaks, but we need to manually
// clean up the standard streams ourselves.
fclose(stdin);
fclose(stdout);
fclose(stderr);
if (libc_malloc_impl_handle != NULL) {
MallocDebugFini malloc_debug_finalize =
reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle,
"malloc_debug_finalize"));
if (malloc_debug_finalize != NULL) {
malloc_debug_finalize(g_malloc_debug_level);
}
if (libc_malloc_impl_handle != NULL) {
MallocDebugFini malloc_debug_finalize =
reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle, "malloc_debug_finalize"));
if (malloc_debug_finalize != NULL) {
malloc_debug_finalize(g_malloc_debug_level);
}
}
}
#endif // !LIBC_STATIC
/* Initializes memory allocation framework.
* This routine is called from __libc_init routines implemented
* in libc_init_static.c and libc_init_dynamic.c files.
*/
// Initializes memory allocation framework.
// This routine is called from __libc_init routines implemented
// in libc_init_static.c and libc_init_dynamic.c files.
extern "C" __LIBC_HIDDEN__ void malloc_debug_init() {
#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
#if !defined(LIBC_STATIC)
static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
error_log("Unable to initialize malloc_debug component.");
}
#endif // USE_DL_PREFIX && !LIBC_STATIC
#endif // !LIBC_STATIC
}
extern "C" __LIBC_HIDDEN__ void malloc_debug_fini() {
#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
#if !defined(LIBC_STATIC)
static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) {
error_log("Unable to finalize malloc_debug component.");
}
#endif // USE_DL_PREFIX && !LIBC_STATIC
#endif // !LIBC_STATIC
}

View file

@ -83,19 +83,27 @@ struct HashTable {
};
/* Entry in malloc dispatch table. */
typedef void* (*MallocDebugMalloc)(size_t);
typedef void (*MallocDebugFree)(void*);
typedef void* (*MallocDebugCalloc)(size_t, size_t);
typedef void* (*MallocDebugRealloc)(void*, size_t);
typedef void* (*MallocDebugMemalign)(size_t, size_t);
typedef void (*MallocDebugFree)(void*);
typedef struct mallinfo (*MallocDebugMallinfo)();
typedef void* (*MallocDebugMalloc)(size_t);
typedef size_t (*MallocDebugMallocUsableSize)(const void*);
typedef void* (*MallocDebugMemalign)(size_t, size_t);
typedef int (*MallocDebugPosixMemalign)(void**, size_t, size_t);
typedef void* (*MallocDebugPvalloc)(size_t);
typedef void* (*MallocDebugRealloc)(void*, size_t);
typedef void* (*MallocDebugValloc)(size_t);
struct MallocDebug {
MallocDebugMalloc malloc;
MallocDebugFree free;
MallocDebugCalloc calloc;
MallocDebugRealloc realloc;
MallocDebugMemalign memalign;
MallocDebugFree free;
MallocDebugMallinfo mallinfo;
MallocDebugMalloc malloc;
MallocDebugMallocUsableSize malloc_usable_size;
MallocDebugMemalign memalign;
MallocDebugPosixMemalign posix_memalign;
MallocDebugPvalloc pvalloc;
MallocDebugRealloc realloc;
MallocDebugValloc valloc;
};
typedef bool (*MallocDebugInit)(HashTable*);

View file

@ -250,6 +250,33 @@ extern "C" size_t fill_malloc_usable_size(const void* mem) {
return Malloc(malloc_usable_size)(mem);
}
extern "C" struct mallinfo fill_mallinfo() {
return Malloc(mallinfo)();
}
extern "C" int fill_posix_memalign(void** memptr, size_t alignment, size_t size) {
if ((alignment & (alignment - 1)) != 0) {
return EINVAL;
}
int saved_errno = errno;
*memptr = fill_memalign(alignment, size);
errno = saved_errno;
return (*memptr != NULL) ? 0 : ENOMEM;
}
extern "C" void* fill_pvalloc(size_t bytes) {
size_t pagesize = sysconf(_SC_PAGESIZE);
size_t size = (bytes + pagesize - 1) & ~(pagesize - 1);
if (size < bytes) { // Overflow
return NULL;
}
return fill_memalign(pagesize, size);
}
extern "C" void* fill_valloc(size_t size) {
return fill_memalign(sysconf(_SC_PAGESIZE), size);
}
// =============================================================================
// malloc leak functions
// =============================================================================
@ -265,6 +292,7 @@ extern "C" void* leak_malloc(size_t bytes) {
size_t size = bytes + sizeof(AllocationEntry);
if (size < bytes) { // Overflow.
errno = ENOMEM;
return NULL;
}
@ -327,6 +355,7 @@ extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
// Fail on overflow - just to be safe even though this code runs only
// within the debugging C library, not the production one.
if (n_elements && SIZE_MAX / n_elements < elem_size) {
errno = ENOMEM;
return NULL;
}
size_t size = n_elements * elem_size;
@ -350,6 +379,7 @@ extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
} else if (header->guard != GUARD) {
debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
header->guard, header->entry);
errno = ENOMEM;
return NULL;
}
@ -358,8 +388,8 @@ extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
size_t oldSize = header->entry->size & ~SIZE_FLAG_MASK;
size_t copySize = (oldSize <= bytes) ? oldSize : bytes;
memcpy(newMem, oldMem, copySize);
leak_free(oldMem);
}
leak_free(oldMem);
return newMem;
}
@ -428,3 +458,30 @@ extern "C" size_t leak_malloc_usable_size(const void* mem) {
}
return 0;
}
extern "C" struct mallinfo leak_mallinfo() {
return Malloc(mallinfo)();
}
extern "C" int leak_posix_memalign(void** memptr, size_t alignment, size_t size) {
if ((alignment & (alignment - 1)) != 0) {
return EINVAL;
}
int saved_errno = errno;
*memptr = leak_memalign(alignment, size);
errno = saved_errno;
return (*memptr != NULL) ? 0 : ENOMEM;
}
extern "C" void* leak_pvalloc(size_t bytes) {
size_t pagesize = sysconf(_SC_PAGESIZE);
size_t size = (bytes + pagesize - 1) & ~(pagesize - 1);
if (size < bytes) { // Overflow
return NULL;
}
return leak_memalign(pagesize, size);
}
extern "C" void* leak_valloc(size_t size) {
return leak_memalign(sysconf(_SC_PAGESIZE), size);
}

View file

@ -573,12 +573,16 @@ static void test_access_violation(const MallocDesc* desc) {
// API routines
// =============================================================================
extern "C" void* qemu_instrumented_malloc(size_t bytes);
extern "C" void qemu_instrumented_free(void* mem);
extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size);
extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes);
extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes);
extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem);
extern "C" void* qemu_instrumented_calloc(size_t, size_t);
extern "C" void qemu_instrumented_free(void*);
extern "C" struct mallinfo qemu_instrumented_mallinfo();
extern "C" void* qemu_instrumented_malloc(size_t);
extern "C" size_t qemu_instrumented_malloc_usable_size(const void*);
extern "C" void* qemu_instrumented_memalign(size_t, size_t);
extern "C" int qemu_instrumented_posix_memalign(void**, size_t, size_t);
extern "C" void* qemu_instrumented_pvalloc(size_t);
extern "C" void* qemu_instrumented_realloc(void*, size_t);
extern "C" void* qemu_instrumented_valloc(size_t);
/* Initializes malloc debugging instrumentation for the emulator.
* This routine is called from malloc_init_impl routine implemented in
@ -680,10 +684,17 @@ extern "C" void* qemu_instrumented_malloc(size_t bytes) {
desc.prefix_size = DEFAULT_PREFIX_SIZE;
desc.requested_bytes = bytes;
desc.suffix_size = DEFAULT_SUFFIX_SIZE;
desc.ptr = Malloc(malloc)(mallocdesc_alloc_size(&desc));
size_t size = mallocdesc_alloc_size(&desc);
if (size < bytes) { // Overflow
qemu_error_log("<libc_pid=%03u, pid=%03u> malloc: malloc(%zu) overflow caused failure.",
malloc_pid, getpid(), bytes);
errno = ENOMEM;
return NULL;
}
desc.ptr = Malloc(malloc)(size);
if (desc.ptr == NULL) {
qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%zd): malloc(%u) failed.",
malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc));
qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%zu): malloc(%u) failed.",
malloc_pid, getpid(), bytes, size);
return NULL;
}
@ -692,12 +703,13 @@ extern "C" void* qemu_instrumented_malloc(size_t bytes) {
log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: malloc: notify_malloc failed for ",
malloc_pid, getpid());
Malloc(free)(desc.ptr);
errno = ENOMEM;
return NULL;
} else {
#if TEST_ACCESS_VIOLATIONS
test_access_violation(&desc);
#endif // TEST_ACCESS_VIOLATIONS
log_mdesc(info, &desc, "+++ <libc_pid=%03u, pid=%03u> malloc(%zd) -> ",
log_mdesc(info, &desc, "+++ <libc_pid=%03u, pid=%03u> malloc(%zu) -> ",
malloc_pid, getpid(), bytes);
return mallocdesc_user_ptr(&desc);
}
@ -754,13 +766,16 @@ extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
if (n_elements == 0 || elem_size == 0) {
// Just let go zero bytes allocation.
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
malloc_pid, getpid());
malloc_pid, getpid());
return qemu_instrumented_malloc(0);
}
// Fail on overflow - just to be safe even though this code runs only
// within the debugging C library, not the production one.
if (n_elements && SIZE_MAX / n_elements < elem_size) {
qemu_error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu, %zu) overflow caused failure.",
malloc_pid, getpid(), n_elements, elem_size);
errno = ENOMEM;
return NULL;
}
@ -786,6 +801,12 @@ extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
}
desc.requested_bytes = n_elements * elem_size;
size_t total_size = desc.requested_bytes + desc.prefix_size + desc.suffix_size;
if (total_size < desc.requested_bytes) { // Overflow
qemu_error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu, %zu) overflow caused failure.",
malloc_pid, getpid(), n_elements, elem_size);
errno = ENOMEM;
return NULL;
}
size_t total_elements = total_size / elem_size;
total_size %= elem_size;
if (total_size != 0) {
@ -795,22 +816,23 @@ extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
}
desc.ptr = Malloc(calloc)(total_elements, elem_size);
if (desc.ptr == NULL) {
error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zd(%zd), %zd) (prx=%u, sfx=%u) failed.",
error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu(%zu), %zu) (prx=%u, sfx=%u) failed.",
malloc_pid, getpid(), n_elements, total_elements, elem_size,
desc.prefix_size, desc.suffix_size);
return NULL;
}
if (notify_qemu_malloc(&desc)) {
log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: calloc(%zd(%zd), %zd): notify_malloc failed for ",
log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: calloc(%zu(%zu), %zu): notify_malloc failed for ",
malloc_pid, getpid(), n_elements, total_elements, elem_size);
Malloc(free)(desc.ptr);
errno = ENOMEM;
return NULL;
} else {
#if TEST_ACCESS_VIOLATIONS
test_access_violation(&desc);
#endif // TEST_ACCESS_VIOLATIONS
log_mdesc(info, &desc, "### <libc_pid=%03u, pid=%03u> calloc(%zd(%zd), %zd) -> ",
log_mdesc(info, &desc, "### <libc_pid=%03u, pid=%03u> calloc(%zu(%zu), %zu) -> ",
malloc_pid, getpid(), n_elements, total_elements, elem_size);
return mallocdesc_user_ptr(&desc);
}
@ -823,22 +845,17 @@ extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
* should not expect that pointer returned after shrinking will remain the same.
*/
extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
MallocDesc new_desc;
MallocDesc cur_desc;
size_t to_copy;
void* ret;
if (mem == NULL) {
// Nothing to realloc. just do regular malloc.
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zd) redir to malloc",
malloc_pid, getpid(), mem, bytes);
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to malloc",
malloc_pid, getpid(), mem, bytes);
return qemu_instrumented_malloc(bytes);
}
if (bytes == 0) {
// This is a "free" condition.
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zd) redir to free and malloc",
malloc_pid, getpid(), mem, bytes);
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to free and malloc",
malloc_pid, getpid(), mem, bytes);
qemu_instrumented_free(mem);
// This is what realloc does for a "free" realloc.
@ -846,10 +863,12 @@ extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
}
// Query emulator for the reallocating block information.
MallocDesc cur_desc;
if (query_qemu_malloc_info(mem, &cur_desc, 2)) {
// Note that this violation should be already caught in the emulator.
error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zd) query_info failed.",
error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) query_info failed.",
malloc_pid, getpid(), mem, bytes);
errno = ENOMEM;
return NULL;
}
@ -861,8 +880,9 @@ extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
* for this memory block. Note that this violation should be already caught
* in the emulator.*/
if (mem != mallocdesc_user_ptr(&cur_desc)) {
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zd) is invalid for ",
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) is invalid for ",
malloc_pid, getpid(), mem, bytes);
errno = ENOMEM;
return NULL;
}
@ -872,31 +892,38 @@ extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
* for this block that is stored in the emulator. */
// Initialize descriptor for the new block.
MallocDesc new_desc;
new_desc.prefix_size = DEFAULT_PREFIX_SIZE;
new_desc.requested_bytes = bytes;
new_desc.suffix_size = DEFAULT_SUFFIX_SIZE;
new_desc.ptr = Malloc(malloc)(mallocdesc_alloc_size(&new_desc));
if (new_desc.ptr == NULL) {
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zd): malloc(%u) failed on ",
malloc_pid, getpid(), mem, bytes,
mallocdesc_alloc_size(&new_desc));
size_t new_size = mallocdesc_alloc_size(&new_desc);
if (new_size < bytes) { // Overflow
qemu_error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): malloc(%u) failed due to overflow",
malloc_pid, getpid(), mem, bytes, new_size);
errno = ENOMEM;
return NULL;
}
ret = mallocdesc_user_ptr(&new_desc);
new_desc.ptr = Malloc(malloc)(new_size);
if (new_desc.ptr == NULL) {
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): malloc(%u) failed on ",
malloc_pid, getpid(), mem, bytes, new_size);
return NULL;
}
void* new_mem = mallocdesc_user_ptr(&new_desc);
// Copy user data from old block to the new one.
to_copy = bytes < cur_desc.requested_bytes ? bytes :
cur_desc.requested_bytes;
size_t to_copy = bytes < cur_desc.requested_bytes ? bytes : cur_desc.requested_bytes;
if (to_copy != 0) {
memcpy(ret, mallocdesc_user_ptr(&cur_desc), to_copy);
memcpy(new_mem, mallocdesc_user_ptr(&cur_desc), to_copy);
}
// Register new block with emulator.
if (notify_qemu_malloc(&new_desc)) {
log_mdesc(error, &new_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zd) notify_malloc failed -> ",
log_mdesc(error, &new_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) notify_malloc failed -> ",
malloc_pid, getpid(), mem, bytes);
log_mdesc(error, &cur_desc, " <- ");
Malloc(free)(new_desc.ptr);
errno = ENOMEM;
return NULL;
}
@ -906,21 +933,22 @@ extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
// Free old block.
if (notify_qemu_free(mem)) {
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zd): notify_free failed for ",
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): notify_free failed for ",
malloc_pid, getpid(), mem, bytes);
/* Since we registered new decriptor with the emulator, we need
* to unregister it before freeing newly allocated block. */
notify_qemu_free(mallocdesc_user_ptr(&new_desc));
Malloc(free)(new_desc.ptr);
errno = ENOMEM;
return NULL;
}
Malloc(free)(cur_desc.ptr);
log_mdesc(info, &new_desc, "=== <libc_pid=%03u, pid=%03u>: realloc(%p, %zd) -> ",
log_mdesc(info, &new_desc, "=== <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) -> ",
malloc_pid, getpid(), mem, bytes);
log_mdesc(info, &cur_desc, " <- ");
return ret;
return new_mem;
}
/* This routine serves as entry point for 'memalign'.
@ -931,28 +959,38 @@ extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes) {
if (bytes == 0) {
// Just let go zero bytes allocation.
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%zx, %zd) redir to malloc",
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%zx, %zu) redir to malloc",
malloc_pid, getpid(), alignment, bytes);
return qemu_instrumented_malloc(0);
}
/* Prefix size for aligned allocation must be equal to the alignment used
* for allocation in order to ensure proper alignment of the returned
* pointer, in case that alignment requirement is greater than prefix
* size. */
desc.prefix_size = alignment > DEFAULT_PREFIX_SIZE ? alignment :
DEFAULT_PREFIX_SIZE;
// Prefix size for aligned allocation must be equal to the alignment used
// for allocation in order to ensure proper alignment of the returned
// pointer. in case that alignment requirement is greater than prefix
// size.
if (alignment < DEFAULT_PREFIX_SIZE) {
alignment = DEFAULT_PREFIX_SIZE;
} else if (alignment & (alignment - 1)) {
alignment = 1L << (31 - __builtin_clz(alignment));
}
desc.prefix_size = alignment;
desc.requested_bytes = bytes;
desc.suffix_size = DEFAULT_SUFFIX_SIZE;
desc.ptr = Malloc(memalign)(desc.prefix_size, mallocdesc_alloc_size(&desc));
size_t size = mallocdesc_alloc_size(&desc);
if (size < bytes) { // Overflow
qemu_error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zu): malloc(%u) failed due to overflow.",
malloc_pid, getpid(), alignment, bytes, size);
return NULL;
}
desc.ptr = Malloc(memalign)(desc.prefix_size, size);
if (desc.ptr == NULL) {
error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zd): malloc(%u) failed.",
malloc_pid, getpid(), alignment, bytes,
mallocdesc_alloc_size(&desc));
error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zu): malloc(%u) failed.",
malloc_pid, getpid(), alignment, bytes, size);
return NULL;
}
if (notify_qemu_malloc(&desc)) {
log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: memalign(%zx, %zd): notify_malloc failed for ",
log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: memalign(%zx, %zu): notify_malloc failed for ",
malloc_pid, getpid(), alignment, bytes);
Malloc(free)(desc.ptr);
return NULL;
@ -962,7 +1000,7 @@ extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes) {
test_access_violation(&desc);
#endif // TEST_ACCESS_VIOLATIONS
log_mdesc(info, &desc, "@@@ <libc_pid=%03u, pid=%03u> memalign(%zx, %zd) -> ",
log_mdesc(info, &desc, "@@@ <libc_pid=%03u, pid=%03u> memalign(%zx, %zu) -> ",
malloc_pid, getpid(), alignment, bytes);
return mallocdesc_user_ptr(&desc);
}
@ -990,3 +1028,34 @@ extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem) {
/* during instrumentation, we can't really report anything more than requested_bytes */
return cur_desc.requested_bytes;
}
extern "C" struct mallinfo qemu_instrumented_mallinfo() {
return Malloc(mallinfo)();
}
extern "C" int qemu_instrumented_posix_memalign(void** memptr, size_t alignment, size_t size) {
if ((alignment & (alignment - 1)) != 0) {
qemu_error_log("<libc_pid=%03u, pid=%03u> posix_memalign(%p, %zu, %zu): invalid alignment.",
malloc_pid, getpid(), memptr, alignment, size);
return EINVAL;
}
int saved_errno = errno;
*memptr = qemu_instrumented_memalign(alignment, size);
errno = saved_errno;
return (*memptr != NULL) ? 0 : ENOMEM;
}
extern "C" void* qemu_instrumented_pvalloc(size_t bytes) {
size_t pagesize = sysconf(_SC_PAGESIZE);
size_t size = (bytes + pagesize - 1) & ~(pagesize - 1);
if (size < bytes) { // Overflow
qemu_error_log("<libc_pid=%03u, pid=%03u> pvalloc(%zu): overflow (%zu).",
malloc_pid, getpid(), bytes, size);
return NULL;
}
return qemu_instrumented_memalign(pagesize, size);
}
extern "C" void* qemu_instrumented_valloc(size_t size) {
return qemu_instrumented_memalign(sysconf(_SC_PAGESIZE), size);
}

View file

@ -5317,12 +5317,19 @@ void* dlvalloc(size_t bytes) {
return dlmemalign(pagesz, bytes);
}
/* BEGIN android-changed: added overflow check */
void* dlpvalloc(size_t bytes) {
size_t pagesz;
size_t size;
ensure_initialization();
pagesz = mparams.page_size;
return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE));
size = (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE);
if (size < bytes) {
return NULL;
}
return dlmemalign(pagesz, size);
}
/* END android-change */
void** dlindependent_calloc(size_t n_elements, size_t elem_size,
void* chunks[]) {

View file

@ -16,18 +16,26 @@
#include <gtest/gtest.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <malloc.h>
#include <unistd.h>
TEST(malloc, malloc_std) {
// Simple malloc test.
void *ptr = malloc(100);
ASSERT_TRUE(ptr != NULL);
ASSERT_LE(100U, malloc_usable_size(ptr));
free(ptr);
}
TEST(malloc, malloc_overflow) {
errno = 0;
ASSERT_EQ(NULL, malloc(SIZE_MAX));
ASSERT_EQ(ENOMEM, errno);
}
TEST(malloc, calloc_std) {
// Simple calloc test.
size_t alloc_len = 100;
@ -37,24 +45,67 @@ TEST(malloc, calloc_std) {
for (size_t i = 0; i < alloc_len; i++) {
ASSERT_EQ(0, ptr[i]);
}
free(ptr);
}
TEST(malloc, calloc_illegal) {
errno = 0;
ASSERT_EQ(NULL, calloc(-1, 100));
ASSERT_EQ(ENOMEM, errno);
}
TEST(malloc, calloc_overflow) {
errno = 0;
ASSERT_EQ(NULL, calloc(1, SIZE_MAX));
ASSERT_EQ(ENOMEM, errno);
errno = 0;
ASSERT_EQ(NULL, calloc(SIZE_MAX, SIZE_MAX));
ASSERT_EQ(ENOMEM, errno);
errno = 0;
ASSERT_EQ(NULL, calloc(2, SIZE_MAX));
ASSERT_EQ(ENOMEM, errno);
errno = 0;
ASSERT_EQ(NULL, calloc(SIZE_MAX, 2));
ASSERT_EQ(ENOMEM, errno);
}
TEST(malloc, memalign_multiple) {
// Memalign test where the alignment is any value.
for (size_t i = 0; i <= 12; i++) {
for (size_t alignment = 1 << i; alignment < (1U << (i+1)); alignment++) {
char *ptr = (char*)memalign(alignment, 100);
ASSERT_TRUE(ptr != NULL) << alignment;
ASSERT_LE(100U, malloc_usable_size(ptr));
ASSERT_EQ(0, (intptr_t)ptr % (1 << i));
char *ptr = reinterpret_cast<char*>(memalign(alignment, 100));
ASSERT_TRUE(ptr != NULL) << "Failed at alignment " << alignment;
ASSERT_LE(100U, malloc_usable_size(ptr)) << "Failed at alignment " << alignment;
ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(ptr) % ((1U << i)))
<< "Failed at alignment " << alignment;
free(ptr);
}
}
}
TEST(malloc, memalign_overflow) {
ASSERT_EQ(NULL, memalign(4096, SIZE_MAX));
}
TEST(malloc, memalign_non_power2) {
void* ptr;
for (size_t align = 0; align <= 256; align++) {
ptr = memalign(align, 1024);
ASSERT_TRUE(ptr != NULL) << "Failed at align " << align;
free(ptr);
}
}
TEST(malloc, posix_memalign_non_power2) {
void* ptr;
ASSERT_EQ(EINVAL, posix_memalign(&ptr, 17, 1024));
}
TEST(malloc, posix_memalign_overflow) {
void* ptr;
ASSERT_NE(0, posix_memalign(&ptr, 16, SIZE_MAX));
}
TEST(malloc, memalign_realloc) {
// Memalign and then realloc the pointer a couple of times.
for (size_t alignment = 1; alignment <= 4096; alignment <<= 1) {
@ -87,7 +138,6 @@ TEST(malloc, memalign_realloc) {
for (size_t i = 0; i < 250; i++) {
ASSERT_EQ(0x67, ptr[i]);
}
free(ptr);
}
}
@ -105,7 +155,6 @@ TEST(malloc, malloc_realloc_larger) {
for (size_t i = 0; i < 100; i++) {
ASSERT_EQ(67, ptr[i]);
}
free(ptr);
}
@ -122,7 +171,6 @@ TEST(malloc, malloc_realloc_smaller) {
for (size_t i = 0; i < 100; i++) {
ASSERT_EQ(67, ptr[i]);
}
free(ptr);
}
@ -161,9 +209,9 @@ TEST(malloc, malloc_multiple_realloc) {
for (size_t i = 0; i < 150; i++) {
ASSERT_EQ(0x23, ptr[i]);
}
free(ptr);
}
TEST(malloc, calloc_realloc_larger) {
// Realloc to a larger size, calloc is used for the original allocation.
char *ptr = (char *)calloc(1, 100);
@ -176,7 +224,6 @@ TEST(malloc, calloc_realloc_larger) {
for (size_t i = 0; i < 100; i++) {
ASSERT_EQ(0, ptr[i]);
}
free(ptr);
}
@ -192,7 +239,6 @@ TEST(malloc, calloc_realloc_smaller) {
for (size_t i = 0; i < 100; i++) {
ASSERT_EQ(0, ptr[i]);
}
free(ptr);
}
@ -230,21 +276,42 @@ TEST(malloc, calloc_multiple_realloc) {
for (size_t i = 0; i < 150; i++) {
ASSERT_EQ(0, ptr[i]);
}
free(ptr);
}
TEST(malloc, posix_memalign_non_power2) {
void* ptr;
ASSERT_EQ(EINVAL, posix_memalign(&ptr, 17, 1024));
TEST(malloc, realloc_overflow) {
errno = 0;
ASSERT_EQ(NULL, realloc(NULL, SIZE_MAX));
ASSERT_EQ(ENOMEM, errno);
void* ptr = malloc(100);
ASSERT_TRUE(ptr != NULL);
errno = 0;
ASSERT_EQ(NULL, realloc(ptr, SIZE_MAX));
ASSERT_EQ(ENOMEM, errno);
free(ptr);
}
TEST(malloc, memalign_non_power2) {
void* ptr;
for (size_t align = 0; align <= 256; align++) {
ptr = memalign(align, 1024);
ASSERT_TRUE(ptr != NULL) << "Failed at align " << align;
free(ptr);
}
TEST(malloc, pvalloc_std) {
size_t pagesize = sysconf(_SC_PAGESIZE);
void* ptr = pvalloc(100);
ASSERT_TRUE(ptr != NULL);
ASSERT_TRUE((reinterpret_cast<uintptr_t>(ptr) & (pagesize-1)) == 0);
ASSERT_LE(pagesize, malloc_usable_size(ptr));
free(ptr);
}
TEST(malloc, pvalloc_overflow) {
ASSERT_EQ(NULL, pvalloc(SIZE_MAX));
}
TEST(malloc, valloc_std) {
size_t pagesize = sysconf(_SC_PAGESIZE);
void* ptr = pvalloc(100);
ASSERT_TRUE(ptr != NULL);
ASSERT_TRUE((reinterpret_cast<uintptr_t>(ptr) & (pagesize-1)) == 0);
free(ptr);
}
TEST(malloc, valloc_overflow) {
ASSERT_EQ(NULL, valloc(SIZE_MAX));
}