Put all allocation functions into dispatch table.

Implement these new functions for all of the debug malloc types.

Fix a number of bugs in the debug malloc functions related to overflow
conditions.
Fix a bug in dlpvalloc due to an overflow condition.

Fix various other bugs in the debug malloc functions.

Add new tests for malloc functions.

Bug: 11225066

Change-Id: Idf50f389603e2157645565bc15cd9365eec2e9dd
This commit is contained in:
Christopher Ferris 2014-06-09 19:14:11 -07:00
parent 0ada9388e7
commit a403780538
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__); // 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) { if (hdr) {
hdr->base = hdr; hdr->base = hdr;
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
add(hdr, size); add(hdr, bytes);
return user(hdr); return user(hdr);
} }
return NULL; 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__); // log_message("%s: %s\n", __FILE__, __FUNCTION__);
if (!ptr) { if (!ptr) {
return chk_malloc(size); return chk_malloc(bytes);
} }
#ifdef REALLOC_ZERO_BYTES_FREE #ifdef REALLOC_ZERO_BYTES_FREE
if (!size) { if (!bytes) {
chk_free(ptr); chk_free(ptr);
return NULL; return NULL;
} }
@ -432,7 +437,7 @@ extern "C" void* chk_realloc(void* ptr, size_t size) {
int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH); int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
if (hdr->tag == BACKLOG_TAG) { if (hdr->tag == BACKLOG_TAG) {
log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n", 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", log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(hdr->bt, hdr->bt_depth); 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); del_from_backlog(hdr);
} else { } else {
log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
user(hdr), size); user(hdr), bytes);
log_backtrace(bt, depth); log_backtrace(bt, depth);
// just get a whole new allocation and leak the old one // just get a whole new allocation and leak the old one
return Malloc(realloc)(0, size); return Malloc(realloc)(0, bytes);
// return realloc(user(hdr), size); // assuming it was allocated externally // 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) { if (hdr->base != hdr) {
// An allocation from memalign, so create another allocation and // An allocation from memalign, so create another allocation and
// copy the data out. // copy the data out.
void* newMem = Malloc(malloc)(sizeof(hdr_t) + size + sizeof(ftr_t)); void* newMem = Malloc(malloc)(size);
if (newMem) { if (newMem == NULL) {
memcpy(newMem, hdr, sizeof(hdr_t) + hdr->size); return NULL;
Malloc(free)(hdr->base);
hdr = static_cast<hdr_t*>(newMem);
} else {
Malloc(free)(hdr->base);
hdr = NULL;
} }
memcpy(newMem, hdr, sizeof(hdr_t) + hdr->size);
Malloc(free)(hdr->base);
hdr = static_cast<hdr_t*>(newMem);
} else { } 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) { if (hdr) {
hdr->base = hdr; hdr->base = hdr;
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
add(hdr, size); add(hdr, bytes);
return user(hdr); return user(hdr);
} }
return NULL; 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__); // log_message("%s: %s\n", __FILE__, __FUNCTION__);
size_t total_size = nmemb * size; size_t total_bytes = nmemb * bytes;
hdr_t* hdr = static_cast<hdr_t*>(Malloc(calloc)(1, sizeof(hdr_t) + total_size + sizeof(ftr_t))); 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) { if (hdr) {
hdr->base = hdr; hdr->base = hdr;
hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH); hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
add(hdr, total_size); add(hdr, total_bytes);
return user(hdr); return user(hdr);
} }
return NULL; return NULL;
@ -509,6 +521,33 @@ extern "C" size_t chk_malloc_usable_size(const void* ptr) {
return hdr->size; 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() { static void ReportMemoryLeaks() {
// Use /proc/self/exe link to obtain the program name for logging // Use /proc/self/exe link to obtain the program name for logging
// purposes. If it's not available, we set it to "<unknown>". // purposes. If it's not available, we set it to "<unknown>".

View File

@ -26,19 +26,17 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/* // Contains definition of structures, global variables, and implementation of
* Contains definition of structures, global variables, and implementation of // routines that are used by malloc leak detection code and other components in
* 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
* the system. The trick is that some components expect these data and // routines to be defined / implemented in libc.so library, regardless
* routines to be defined / implemented in libc.so library, regardless // whether or not MALLOC_LEAK_CHECK macro is defined. To make things even
* whether or not MALLOC_LEAK_CHECK macro is defined. To make things even // more tricky, malloc leak detection code, implemented in
* more tricky, malloc leak detection code, implemented in // libc_malloc_debug.so also requires access to these variables and routines
* libc_malloc_debug.so also requires access to these variables and routines // (to fill allocation entry hash table, for example). So, all relevant
* (to fill allocation entry hash table, for example). So, all relevant // variables and routines are defined / implemented here and exported
* variables and routines are defined / implemented here and exported // to all, leak detection code and other components via dynamic (libc.so),
* to all, leak detection code and other components via dynamic (libc.so), // or static (libc.a) linking.
* or static (libc.a) linking.
*/
#include "malloc_debug_common.h" #include "malloc_debug_common.h"
@ -56,7 +54,16 @@ static HashTable g_hash_table;
// Support for malloc debugging. // Support for malloc debugging.
// Table for dispatching malloc calls, initialized with default dispatchers. // Table for dispatching malloc calls, initialized with default dispatchers.
static const MallocDebug __libc_malloc_default_dispatch __attribute__((aligned(32))) = { 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. // 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) { 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* e1 = *static_cast<HashEntry* const*>(arg1);
const HashEntry* e2 = *static_cast<HashEntry* const*>(arg2); const HashEntry* e2 = *static_cast<HashEntry* const*>(arg2);
// if one or both arg pointers are null, deal gracefully // if one or both arg pointers are null, deal gracefully
if (e1 == NULL) { if (e1 == NULL) {
result = (e2 == NULL) ? 0 : 1; result = (e2 == NULL) ? 0 : 1;
} else if (e2 == NULL) { } else if (e2 == NULL) {
result = -1; 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 { } else {
size_t nbAlloc1 = e1->allocations; if (nbAlloc1 > nbAlloc2) {
size_t nbAlloc2 = e2->allocations; result = -1;
size_t size1 = e1->size & ~SIZE_FLAG_MASK; } else if (nbAlloc1 < nbAlloc2) {
size_t size2 = e2->size & ~SIZE_FLAG_MASK; result = 1;
size_t alloc1 = nbAlloc1 * size1; } else {
size_t alloc2 = nbAlloc2 * size2; result = 0;
}
// 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;
}
}
} }
return result; }
return result;
} }
/* // Retrieve native heap information.
* Retrieve native heap information. //
* // "*info" is set to a buffer we allocate
* "*info" is set to a buffer we allocate // "*overallSize" is set to the size of the "info" buffer
* "*overallSize" is set to the size of the "info" buffer // "*infoSize" is set to the size of a single entry
* "*infoSize" is set to the size of a single entry // "*totalMemory" is set to the sum of all allocations we're tracking; does
* "*totalMemory" is set to the sum of all allocations we're tracking; does // not include heap overhead
* not include heap overhead // "*backtraceSize" is set to the maximum number of entries in the back trace
* "*backtraceSize" is set to the maximum number of entries in the back trace
*/
// =============================================================================
// Exported for use by ddms. // Exported for use by ddms.
// =============================================================================
extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
size_t* infoSize, size_t* totalMemory, size_t* backtraceSize) { size_t* infoSize, size_t* totalMemory, size_t* backtraceSize) {
// don't do anything if we have invalid arguments // Don't do anything if we have invalid arguments.
if (info == NULL || overallSize == NULL || infoSize == NULL || if (info == NULL || overallSize == NULL || infoSize == NULL ||
totalMemory == NULL || backtraceSize == NULL) { totalMemory == NULL || backtraceSize == NULL) {
return; 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); // XXX: the protocol doesn't allow variable size for the stack trace (yet)
*infoSize = (sizeof(size_t) * 2) + (sizeof(uintptr_t) * BACKTRACE_SIZE);
if (g_hash_table.count == 0) { *overallSize = *infoSize * g_hash_table.count;
*info = NULL; *backtraceSize = BACKTRACE_SIZE;
*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;
}
// 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); 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) { extern "C" void free_malloc_leak_info(uint8_t* info) {
Malloc(free)(info); Malloc(free)(info);
} }
extern "C" struct mallinfo mallinfo() { // =============================================================================
return Malloc(mallinfo)(); // Allocation functions
} // =============================================================================
extern "C" void* calloc(size_t n_elements, size_t elem_size) {
extern "C" void* valloc(size_t bytes) { return __libc_malloc_dispatch->calloc(n_elements, elem_size);
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);
} }
extern "C" void free(void* mem) { 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) { extern "C" struct mallinfo mallinfo() {
return __libc_malloc_dispatch->calloc(n_elements, elem_size); return __libc_malloc_dispatch->mallinfo();
} }
extern "C" void* realloc(void* oldMem, size_t bytes) { extern "C" void* malloc(size_t bytes) {
return __libc_malloc_dispatch->realloc(oldMem, bytes); return __libc_malloc_dispatch->malloc(bytes);
}
extern "C" void* memalign(size_t alignment, size_t bytes) {
return __libc_malloc_dispatch->memalign(alignment, bytes);
} }
extern "C" size_t malloc_usable_size(const void* mem) { 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 extern "C" void* memalign(size_t alignment, size_t bytes) {
* must be excluded if we compile this file for static libc.a 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 #ifndef LIBC_STATIC
#include <sys/system_properties.h> #include <sys/system_properties.h>
#include <dlfcn.h> #include <dlfcn.h>
@ -275,227 +280,222 @@ extern "C" size_t malloc_usable_size(const void* mem) {
template<typename FunctionType> template<typename FunctionType>
static void InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) { static void InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
char symbol[128]; char symbol[128];
snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix); snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
*func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol)); *func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
if (*func == NULL) { if (*func == NULL) {
error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol); error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
} }
} }
static void InitMalloc(void* malloc_impl_handler, MallocDebug* table, const char* prefix) { 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", __libc_format_log(ANDROID_LOG_INFO, "libc", "%s: using libc.debug.malloc %d (%s)\n",
getprogname(), g_malloc_debug_level, prefix); getprogname(), g_malloc_debug_level, prefix);
InitMallocFunction<MallocDebugMalloc>(malloc_impl_handler, &table->malloc, prefix, "malloc"); InitMallocFunction<MallocDebugCalloc>(malloc_impl_handler, &table->calloc, prefix, "calloc");
InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free"); InitMallocFunction<MallocDebugFree>(malloc_impl_handler, &table->free, prefix, "free");
InitMallocFunction<MallocDebugCalloc>(malloc_impl_handler, &table->calloc, prefix, "calloc"); InitMallocFunction<MallocDebugMallinfo>(malloc_impl_handler, &table->mallinfo, prefix, "mallinfo");
InitMallocFunction<MallocDebugRealloc>(malloc_impl_handler, &table->realloc, prefix, "realloc"); InitMallocFunction<MallocDebugMalloc>(malloc_impl_handler, &table->malloc, prefix, "malloc");
InitMallocFunction<MallocDebugMemalign>(malloc_impl_handler, &table->memalign, prefix, "memalign"); InitMallocFunction<MallocDebugMallocUsableSize>(malloc_impl_handler, &table->malloc_usable_size, prefix, "malloc_usable_size");
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() { static void malloc_init_impl() {
const char* so_name = NULL; const char* so_name = NULL;
MallocDebugInit malloc_debug_initialize = NULL; MallocDebugInit malloc_debug_initialize = NULL;
unsigned int qemu_running = 0; unsigned int qemu_running = 0;
unsigned int memcheck_enabled = 0; unsigned int memcheck_enabled = 0;
char env[PROP_VALUE_MAX]; char env[PROP_VALUE_MAX];
char memcheck_tracing[PROP_VALUE_MAX]; char memcheck_tracing[PROP_VALUE_MAX];
char debug_program[PROP_VALUE_MAX]; char debug_program[PROP_VALUE_MAX];
/* Get custom malloc debug level. Note that emulator started with // Get custom malloc debug level. Note that emulator started with
* memory checking option will have priority over debug level set in // memory checking option will have priority over debug level set in
* libc.debug.malloc system property. */ // libc.debug.malloc system property.
if (__system_property_get("ro.kernel.qemu", env) && atoi(env)) { if (__system_property_get("ro.kernel.qemu", env) && atoi(env)) {
qemu_running = 1; qemu_running = 1;
if (__system_property_get("ro.kernel.memcheck", memcheck_tracing)) { if (__system_property_get("ro.kernel.memcheck", memcheck_tracing)) {
if (memcheck_tracing[0] != '0') { if (memcheck_tracing[0] != '0') {
// Emulator has started with memory tracing enabled. Enforce it. // Emulator has started with memory tracing enabled. Enforce it.
g_malloc_debug_level = 20; g_malloc_debug_level = 20;
memcheck_enabled = 1; memcheck_enabled = 1;
} }
}
} }
}
/* If debug level has not been set by memcheck option in the emulator, // If debug level has not been set by memcheck option in the emulator,
* lets grab it from libc.debug.malloc system property. */ // lets grab it from libc.debug.malloc system property.
if (g_malloc_debug_level == 0 && __system_property_get("libc.debug.malloc", env)) { if (g_malloc_debug_level == 0 && __system_property_get("libc.debug.malloc", env)) {
g_malloc_debug_level = atoi(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. */ // mksh is way too leaky. http://b/7291287.
if (g_malloc_debug_level == 0) { if (g_malloc_debug_level >= 10) {
return; 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, // Choose the appropriate .so for the requested debug level.
* then exit. switch (g_malloc_debug_level) {
*/ case 1:
if (__system_property_get("libc.debug.malloc.program", debug_program)) { case 5:
if (!strstr(getprogname(), debug_program)) { case 10:
return; so_name = "libc_malloc_debug_leak.so";
} break;
} case 20:
// Quick check: debug level 20 can only be handled in emulator.
// mksh is way too leaky. http://b/7291287. if (!qemu_running) {
if (g_malloc_debug_level >= 10) { error_log("%s: Debug level %d can only be set in emulator\n",
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)",
getprogname(), g_malloc_debug_level); getprogname(), g_malloc_debug_level);
dlclose(malloc_impl_handle); return;
} else { }
__libc_malloc_dispatch = &malloc_dispatch_table; // Make sure that memory checking has been enabled in emulator.
libc_malloc_impl_handle = malloc_impl_handle; 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() { static void malloc_fini_impl() {
// Our BSD stdio implementation doesn't close the standard streams, it only flushes them. // 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! // 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 // It's great that other unclosed FILE*s show up as malloc leaks, but we need to manually
// clean up the standard streams ourselves. // clean up the standard streams ourselves.
fclose(stdin); fclose(stdin);
fclose(stdout); fclose(stdout);
fclose(stderr); fclose(stderr);
if (libc_malloc_impl_handle != NULL) { if (libc_malloc_impl_handle != NULL) {
MallocDebugFini malloc_debug_finalize = MallocDebugFini malloc_debug_finalize =
reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle, reinterpret_cast<MallocDebugFini>(dlsym(libc_malloc_impl_handle, "malloc_debug_finalize"));
"malloc_debug_finalize")); if (malloc_debug_finalize != NULL) {
if (malloc_debug_finalize != NULL) { malloc_debug_finalize(g_malloc_debug_level);
malloc_debug_finalize(g_malloc_debug_level);
}
} }
}
} }
#endif // !LIBC_STATIC #endif // !LIBC_STATIC
/* Initializes memory allocation framework. // Initializes memory allocation framework.
* This routine is called from __libc_init routines implemented // This routine is called from __libc_init routines implemented
* in libc_init_static.c and libc_init_dynamic.c files. // in libc_init_static.c and libc_init_dynamic.c files.
*/
extern "C" __LIBC_HIDDEN__ void malloc_debug_init() { 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; static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) { if (pthread_once(&malloc_init_once_ctl, malloc_init_impl)) {
error_log("Unable to initialize malloc_debug component."); 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() { 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; static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) { if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) {
error_log("Unable to finalize malloc_debug component."); 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. */ /* Entry in malloc dispatch table. */
typedef void* (*MallocDebugMalloc)(size_t);
typedef void (*MallocDebugFree)(void*);
typedef void* (*MallocDebugCalloc)(size_t, size_t); typedef void* (*MallocDebugCalloc)(size_t, size_t);
typedef void* (*MallocDebugRealloc)(void*, size_t); typedef void (*MallocDebugFree)(void*);
typedef void* (*MallocDebugMemalign)(size_t, size_t); typedef struct mallinfo (*MallocDebugMallinfo)();
typedef void* (*MallocDebugMalloc)(size_t);
typedef size_t (*MallocDebugMallocUsableSize)(const void*); 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 { struct MallocDebug {
MallocDebugMalloc malloc;
MallocDebugFree free;
MallocDebugCalloc calloc; MallocDebugCalloc calloc;
MallocDebugRealloc realloc; MallocDebugFree free;
MallocDebugMemalign memalign; MallocDebugMallinfo mallinfo;
MallocDebugMalloc malloc;
MallocDebugMallocUsableSize malloc_usable_size; MallocDebugMallocUsableSize malloc_usable_size;
MallocDebugMemalign memalign;
MallocDebugPosixMemalign posix_memalign;
MallocDebugPvalloc pvalloc;
MallocDebugRealloc realloc;
MallocDebugValloc valloc;
}; };
typedef bool (*MallocDebugInit)(HashTable*); 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); 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 // malloc leak functions
// ============================================================================= // =============================================================================
@ -265,6 +292,7 @@ extern "C" void* leak_malloc(size_t bytes) {
size_t size = bytes + sizeof(AllocationEntry); size_t size = bytes + sizeof(AllocationEntry);
if (size < bytes) { // Overflow. if (size < bytes) { // Overflow.
errno = ENOMEM;
return NULL; 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 // Fail on overflow - just to be safe even though this code runs only
// within the debugging C library, not the production one. // within the debugging C library, not the production one.
if (n_elements && SIZE_MAX / n_elements < elem_size) { if (n_elements && SIZE_MAX / n_elements < elem_size) {
errno = ENOMEM;
return NULL; return NULL;
} }
size_t size = n_elements * elem_size; 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) { } else if (header->guard != GUARD) {
debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n", debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
header->guard, header->entry); header->guard, header->entry);
errno = ENOMEM;
return NULL; 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 oldSize = header->entry->size & ~SIZE_FLAG_MASK;
size_t copySize = (oldSize <= bytes) ? oldSize : bytes; size_t copySize = (oldSize <= bytes) ? oldSize : bytes;
memcpy(newMem, oldMem, copySize); memcpy(newMem, oldMem, copySize);
leak_free(oldMem);
} }
leak_free(oldMem);
return newMem; return newMem;
} }
@ -428,3 +458,30 @@ extern "C" size_t leak_malloc_usable_size(const void* mem) {
} }
return 0; 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 // API routines
// ============================================================================= // =============================================================================
extern "C" void* qemu_instrumented_malloc(size_t bytes); extern "C" void* qemu_instrumented_calloc(size_t, size_t);
extern "C" void qemu_instrumented_free(void* mem); extern "C" void qemu_instrumented_free(void*);
extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size); extern "C" struct mallinfo qemu_instrumented_mallinfo();
extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes); extern "C" void* qemu_instrumented_malloc(size_t);
extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes); extern "C" size_t qemu_instrumented_malloc_usable_size(const void*);
extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem); 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. /* Initializes malloc debugging instrumentation for the emulator.
* This routine is called from malloc_init_impl routine implemented in * 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.prefix_size = DEFAULT_PREFIX_SIZE;
desc.requested_bytes = bytes; desc.requested_bytes = bytes;
desc.suffix_size = DEFAULT_SUFFIX_SIZE; 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) { if (desc.ptr == NULL) {
qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%zd): malloc(%u) failed.", qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%zu): malloc(%u) failed.",
malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc)); malloc_pid, getpid(), bytes, size);
return NULL; 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 ", log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: malloc: notify_malloc failed for ",
malloc_pid, getpid()); malloc_pid, getpid());
Malloc(free)(desc.ptr); Malloc(free)(desc.ptr);
errno = ENOMEM;
return NULL; return NULL;
} else { } else {
#if TEST_ACCESS_VIOLATIONS #if TEST_ACCESS_VIOLATIONS
test_access_violation(&desc); test_access_violation(&desc);
#endif // TEST_ACCESS_VIOLATIONS #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); malloc_pid, getpid(), bytes);
return mallocdesc_user_ptr(&desc); 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) { if (n_elements == 0 || elem_size == 0) {
// Just let go zero bytes allocation. // Just let go zero bytes allocation.
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc", qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
malloc_pid, getpid()); malloc_pid, getpid());
return qemu_instrumented_malloc(0); return qemu_instrumented_malloc(0);
} }
// Fail on overflow - just to be safe even though this code runs only // Fail on overflow - just to be safe even though this code runs only
// within the debugging C library, not the production one. // within the debugging C library, not the production one.
if (n_elements && SIZE_MAX / n_elements < elem_size) { 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; 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; desc.requested_bytes = n_elements * elem_size;
size_t total_size = desc.requested_bytes + desc.prefix_size + desc.suffix_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; size_t total_elements = total_size / elem_size;
total_size %= elem_size; total_size %= elem_size;
if (total_size != 0) { 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); desc.ptr = Malloc(calloc)(total_elements, elem_size);
if (desc.ptr == NULL) { 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, malloc_pid, getpid(), n_elements, total_elements, elem_size,
desc.prefix_size, desc.suffix_size); desc.prefix_size, desc.suffix_size);
return NULL; return NULL;
} }
if (notify_qemu_malloc(&desc)) { 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_pid, getpid(), n_elements, total_elements, elem_size);
Malloc(free)(desc.ptr); Malloc(free)(desc.ptr);
errno = ENOMEM;
return NULL; return NULL;
} else { } else {
#if TEST_ACCESS_VIOLATIONS #if TEST_ACCESS_VIOLATIONS
test_access_violation(&desc); test_access_violation(&desc);
#endif // TEST_ACCESS_VIOLATIONS #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); malloc_pid, getpid(), n_elements, total_elements, elem_size);
return mallocdesc_user_ptr(&desc); 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. * should not expect that pointer returned after shrinking will remain the same.
*/ */
extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) { 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) { if (mem == NULL) {
// Nothing to realloc. just do regular malloc. // Nothing to realloc. just do regular malloc.
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zd) redir to malloc", qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to malloc",
malloc_pid, getpid(), mem, bytes); malloc_pid, getpid(), mem, bytes);
return qemu_instrumented_malloc(bytes); return qemu_instrumented_malloc(bytes);
} }
if (bytes == 0) { if (bytes == 0) {
// This is a "free" condition. // This is a "free" condition.
qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zd) redir to free and malloc", qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to free and malloc",
malloc_pid, getpid(), mem, bytes); malloc_pid, getpid(), mem, bytes);
qemu_instrumented_free(mem); qemu_instrumented_free(mem);
// This is what realloc does for a "free" realloc. // 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. // Query emulator for the reallocating block information.
MallocDesc cur_desc;
if (query_qemu_malloc_info(mem, &cur_desc, 2)) { if (query_qemu_malloc_info(mem, &cur_desc, 2)) {
// Note that this violation should be already caught in the emulator. // 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); malloc_pid, getpid(), mem, bytes);
errno = ENOMEM;
return NULL; 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 * for this memory block. Note that this violation should be already caught
* in the emulator.*/ * in the emulator.*/
if (mem != mallocdesc_user_ptr(&cur_desc)) { 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); malloc_pid, getpid(), mem, bytes);
errno = ENOMEM;
return NULL; 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. */ * for this block that is stored in the emulator. */
// Initialize descriptor for the new block. // Initialize descriptor for the new block.
MallocDesc new_desc;
new_desc.prefix_size = DEFAULT_PREFIX_SIZE; new_desc.prefix_size = DEFAULT_PREFIX_SIZE;
new_desc.requested_bytes = bytes; new_desc.requested_bytes = bytes;
new_desc.suffix_size = DEFAULT_SUFFIX_SIZE; new_desc.suffix_size = DEFAULT_SUFFIX_SIZE;
new_desc.ptr = Malloc(malloc)(mallocdesc_alloc_size(&new_desc)); size_t new_size = mallocdesc_alloc_size(&new_desc);
if (new_desc.ptr == NULL) { if (new_size < bytes) { // Overflow
log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zd): malloc(%u) failed on ", qemu_error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): malloc(%u) failed due to overflow",
malloc_pid, getpid(), mem, bytes, malloc_pid, getpid(), mem, bytes, new_size);
mallocdesc_alloc_size(&new_desc)); errno = ENOMEM;
return NULL; 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. // Copy user data from old block to the new one.
to_copy = bytes < cur_desc.requested_bytes ? bytes : size_t to_copy = bytes < cur_desc.requested_bytes ? bytes : cur_desc.requested_bytes;
cur_desc.requested_bytes;
if (to_copy != 0) { 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. // Register new block with emulator.
if (notify_qemu_malloc(&new_desc)) { 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); malloc_pid, getpid(), mem, bytes);
log_mdesc(error, &cur_desc, " <- "); log_mdesc(error, &cur_desc, " <- ");
Malloc(free)(new_desc.ptr); Malloc(free)(new_desc.ptr);
errno = ENOMEM;
return NULL; return NULL;
} }
@ -906,21 +933,22 @@ extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
// Free old block. // Free old block.
if (notify_qemu_free(mem)) { 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); malloc_pid, getpid(), mem, bytes);
/* Since we registered new decriptor with the emulator, we need /* Since we registered new decriptor with the emulator, we need
* to unregister it before freeing newly allocated block. */ * to unregister it before freeing newly allocated block. */
notify_qemu_free(mallocdesc_user_ptr(&new_desc)); notify_qemu_free(mallocdesc_user_ptr(&new_desc));
Malloc(free)(new_desc.ptr); Malloc(free)(new_desc.ptr);
errno = ENOMEM;
return NULL; return NULL;
} }
Malloc(free)(cur_desc.ptr); 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); malloc_pid, getpid(), mem, bytes);
log_mdesc(info, &cur_desc, " <- "); log_mdesc(info, &cur_desc, " <- ");
return ret; return new_mem;
} }
/* This routine serves as entry point for 'memalign'. /* 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) { if (bytes == 0) {
// Just let go zero bytes allocation. // 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); malloc_pid, getpid(), alignment, bytes);
return qemu_instrumented_malloc(0); return qemu_instrumented_malloc(0);
} }
/* Prefix size for aligned allocation must be equal to the alignment used // Prefix size for aligned allocation must be equal to the alignment used
* for allocation in order to ensure proper alignment of the returned // for allocation in order to ensure proper alignment of the returned
* pointer, in case that alignment requirement is greater than prefix // pointer. in case that alignment requirement is greater than prefix
* size. */ // size.
desc.prefix_size = alignment > DEFAULT_PREFIX_SIZE ? alignment : if (alignment < DEFAULT_PREFIX_SIZE) {
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.requested_bytes = bytes;
desc.suffix_size = DEFAULT_SUFFIX_SIZE; 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) { if (desc.ptr == NULL) {
error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zd): malloc(%u) failed.", error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zu): malloc(%u) failed.",
malloc_pid, getpid(), alignment, bytes, malloc_pid, getpid(), alignment, bytes, size);
mallocdesc_alloc_size(&desc));
return NULL; return NULL;
} }
if (notify_qemu_malloc(&desc)) { 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_pid, getpid(), alignment, bytes);
Malloc(free)(desc.ptr); Malloc(free)(desc.ptr);
return NULL; return NULL;
@ -962,7 +1000,7 @@ extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes) {
test_access_violation(&desc); test_access_violation(&desc);
#endif // TEST_ACCESS_VIOLATIONS #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); malloc_pid, getpid(), alignment, bytes);
return mallocdesc_user_ptr(&desc); 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 */ /* during instrumentation, we can't really report anything more than requested_bytes */
return cur_desc.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); return dlmemalign(pagesz, bytes);
} }
/* BEGIN android-changed: added overflow check */
void* dlpvalloc(size_t bytes) { void* dlpvalloc(size_t bytes) {
size_t pagesz; size_t pagesz;
size_t size;
ensure_initialization(); ensure_initialization();
pagesz = mparams.page_size; 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** dlindependent_calloc(size_t n_elements, size_t elem_size,
void* chunks[]) { void* chunks[]) {

View File

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