Merge "Put all allocation functions into dispatch table."
This commit is contained in:
commit
29ddd78c21
7 changed files with 698 additions and 451 deletions
|
@ -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>".
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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[]) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue