2009-03-04 04:28:35 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* * Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
2012-03-14 07:48:39 +01:00
|
|
|
|
2012-10-12 01:08:51 +02:00
|
|
|
#include <arpa/inet.h>
|
2012-03-14 07:48:39 +01:00
|
|
|
#include <dlfcn.h>
|
2009-03-04 04:28:35 +01:00
|
|
|
#include <errno.h>
|
2012-03-14 07:48:39 +01:00
|
|
|
#include <fcntl.h>
|
2009-03-04 04:28:35 +01:00
|
|
|
#include <pthread.h>
|
2012-03-14 07:48:39 +01:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
2009-03-04 04:28:35 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/select.h>
|
2012-03-14 07:48:39 +01:00
|
|
|
#include <sys/socket.h>
|
2009-03-04 04:28:35 +01:00
|
|
|
#include <sys/system_properties.h>
|
2012-03-14 07:48:39 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/un.h>
|
2012-10-12 01:08:51 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <unwind.h>
|
2009-03-04 04:28:35 +01:00
|
|
|
|
2013-01-18 03:36:06 +01:00
|
|
|
#include "debug_stacktrace.h"
|
2009-03-04 04:28:35 +01:00
|
|
|
#include "dlmalloc.h"
|
|
|
|
#include "logd.h"
|
2009-11-17 23:13:38 +01:00
|
|
|
#include "malloc_debug_common.h"
|
2012-10-12 01:08:51 +02:00
|
|
|
#include "ScopedPthreadMutexLocker.h"
|
2009-03-04 04:28:35 +01:00
|
|
|
|
2009-11-17 23:13:38 +01:00
|
|
|
// This file should be included into the build only when
|
|
|
|
// MALLOC_LEAK_CHECK, or MALLOC_QEMU_INSTRUMENT, or both
|
|
|
|
// macros are defined.
|
|
|
|
#ifndef MALLOC_LEAK_CHECK
|
|
|
|
#error MALLOC_LEAK_CHECK is not defined.
|
|
|
|
#endif // !MALLOC_LEAK_CHECK
|
2009-03-04 04:28:35 +01:00
|
|
|
|
2009-11-17 23:13:38 +01:00
|
|
|
// Global variables defined in malloc_debug_common.c
|
|
|
|
extern int gMallocLeakZygoteChild;
|
|
|
|
extern pthread_mutex_t gAllocationsMutex;
|
|
|
|
extern HashTable gHashTable;
|
2009-03-04 04:28:35 +01:00
|
|
|
|
|
|
|
// =============================================================================
|
bionic: import heaptracker as chk_malloc
This patch is a rewrite of libc.debug.malloc = 10 (chk_malloc). It provides
the same features as the original (poison freed memory, detect heap overruns
and underruns), except that it provides more debugging information whenever it
detects a problem.
In addition to the original features, the new chk_malloc() implementation
detects multiple frees within a given range of the last N allocations, N being
configurable via the system property libc.debug.malloc.backlog.
Finally, this patch keeps track of all outstanding memory allocations. On
program exit, we walk that list and report each outstanding allocation.
(There is support (not enabled) for a scanner thread periodically walks over
the list of outstanding allocations as well as the backlog of recently-freed
allocations, checking for heap-usage errors.)
Feature overview:
1) memory leaks
2) multiple frees
3) use after free
4) overrun
Implementation:
-- for each allocation, there is a:
1) stack trace at the time the allocation is made
2) if the memory is freed, there is also a stack trace at the point
3) a front and rear guard (fence)
4) the stack traces are kept together with the allocation
-- the following lists and maintained
1) all outstanding memory allocations
3) a backlog of allocations what are freed; when you call free(), instead of
actually freed, the allocation is moved to this backlog;
4) when the backlog of allocations gets full, the oldest entry gets evicted
from it; at that point, the allocation is checked for overruns or
use-after-free errors, and then actually freed.
5) when the program exits, the list of outstanding allocations and the
backlog are inspected for errors, then freed;
To use this, set the following system properties before running the process or
processes you want to inspect:
libc.malloc.debug.backlog # defaults to 100
libc.malloc.debug 10
When a problem is detected, you will see the following on logcat for a multiple
free:
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 BYTES MULTIPLY FREED!
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 4009647c /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 FIRST FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c7d2 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 40096490 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 NOW BEING FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c6ac /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 400964a0 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
The following for a heap overrun and underrun:
E/libc ( 7233): +++ REAR GUARD MISMATCH [10, 11)
E/libc ( 7233): +++ ALLOCATION 0x404b9198 SIZE 10 HAS A CORRUPTED REAR GUARD
E/libc ( 7233): +++ ALLOCATION 0x404b9198 SIZE 10 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 40096438 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9198 SIZE 10 FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c7d2 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 40096462 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9358 SIZE 10 HAS A CORRUPTED FRONT GUARD
E/libc ( 7233): +++ ALLOCATION 0x404b9358 SIZE 10 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 400964ba /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9358 SIZE 10 FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c7d2 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 400964e4 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
The following for a memory leak:
E/libc ( 7233): +++ THERE ARE 1 LEAKED ALLOCATIONS
E/libc ( 7233): +++ DELETING 4096 BYTES OF LEAKED MEMORY AT 0x404b95e8 (1 REMAINING)
E/libc ( 7233): +++ ALLOCATION 0x404b95e8 SIZE 4096 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 0001bc94 /system/lib/libc.so
E/libc ( 7233): #04 pc 0001edf6 /system/lib/libc.so
E/libc ( 7233): #05 pc 0001b80a /system/lib/libc.so
E/libc ( 7233): #06 pc 0001c086 /system/lib/libc.so
E/libc ( 7233): #07 pc 40096402 /system/bin/malloctest
E/libc ( 7233): #08 pc 00016f24 /system/lib/libc.so
Change-Id: Ic440e9d05a01e2ea86b25e8998714e88bc2d16e0
Signed-off-by: Iliyan Malchev <malchev@google.com>
2012-05-29 23:22:42 +02:00
|
|
|
// stack trace functions
|
2009-07-22 00:25:23 +02:00
|
|
|
// =============================================================================
|
|
|
|
|
2012-08-01 08:48:57 +02:00
|
|
|
#ifndef MALLOC_ALIGNMENT
|
|
|
|
#define MALLOC_ALIGNMENT ((size_t)8U)
|
|
|
|
#endif
|
2009-03-04 04:28:35 +01:00
|
|
|
#define GUARD 0x48151642
|
|
|
|
#define DEBUG 0
|
|
|
|
|
|
|
|
// =============================================================================
|
|
|
|
// Structures
|
|
|
|
// =============================================================================
|
2012-08-28 23:15:04 +02:00
|
|
|
|
2009-03-04 04:28:35 +01:00
|
|
|
struct AllocationEntry {
|
|
|
|
HashEntry* entry;
|
|
|
|
uint32_t guard;
|
|
|
|
};
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
static AllocationEntry* to_header(void* mem) {
|
|
|
|
return reinterpret_cast<AllocationEntry*>(mem) - 1;
|
|
|
|
}
|
2009-03-04 04:28:35 +01:00
|
|
|
|
|
|
|
// =============================================================================
|
|
|
|
// Hash Table functions
|
|
|
|
// =============================================================================
|
2012-08-28 23:15:04 +02:00
|
|
|
|
2013-01-26 02:13:45 +01:00
|
|
|
static uint32_t get_hash(uintptr_t* backtrace, size_t numEntries) {
|
2009-03-04 04:28:35 +01:00
|
|
|
if (backtrace == NULL) return 0;
|
|
|
|
|
|
|
|
int hash = 0;
|
|
|
|
size_t i;
|
|
|
|
for (i = 0 ; i < numEntries ; i++) {
|
|
|
|
hash = (hash * 33) + (backtrace[i] >> 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HashEntry* find_entry(HashTable* table, int slot,
|
2013-01-26 02:13:45 +01:00
|
|
|
uintptr_t* backtrace, size_t numEntries, size_t size) {
|
2009-03-04 04:28:35 +01:00
|
|
|
HashEntry* entry = table->slots[slot];
|
|
|
|
while (entry != NULL) {
|
|
|
|
//debug_log("backtrace: %p, entry: %p entry->backtrace: %p\n",
|
|
|
|
// backtrace, entry, (entry != NULL) ? entry->backtrace : NULL);
|
|
|
|
/*
|
|
|
|
* See if the entry matches exactly. We compare the "size" field,
|
|
|
|
* including the flag bits.
|
|
|
|
*/
|
|
|
|
if (entry->size == size && entry->numEntries == numEntries &&
|
2013-01-26 02:13:45 +01:00
|
|
|
!memcmp(backtrace, entry->backtrace, numEntries * sizeof(uintptr_t))) {
|
2009-03-04 04:28:35 +01:00
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = entry->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-26 02:13:45 +01:00
|
|
|
static HashEntry* record_backtrace(uintptr_t* backtrace, size_t numEntries, size_t size) {
|
2009-03-04 04:28:35 +01:00
|
|
|
size_t hash = get_hash(backtrace, numEntries);
|
|
|
|
size_t slot = hash % HASHTABLE_SIZE;
|
|
|
|
|
|
|
|
if (size & SIZE_FLAG_MASK) {
|
|
|
|
debug_log("malloc_debug: allocation %zx exceeds bit width\n", size);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
if (gMallocLeakZygoteChild) {
|
2009-03-04 04:28:35 +01:00
|
|
|
size |= SIZE_FLAG_ZYGOTE_CHILD;
|
2012-08-28 23:15:04 +02:00
|
|
|
}
|
2009-03-04 04:28:35 +01:00
|
|
|
|
|
|
|
HashEntry* entry = find_entry(&gHashTable, slot, backtrace, numEntries, size);
|
|
|
|
|
|
|
|
if (entry != NULL) {
|
|
|
|
entry->allocations++;
|
|
|
|
} else {
|
|
|
|
// create a new entry
|
2013-01-26 02:13:45 +01:00
|
|
|
entry = static_cast<HashEntry*>(dlmalloc(sizeof(HashEntry) + numEntries*sizeof(uintptr_t)));
|
2012-08-28 23:15:04 +02:00
|
|
|
if (!entry) {
|
2010-02-05 19:03:09 +01:00
|
|
|
return NULL;
|
2012-08-28 23:15:04 +02:00
|
|
|
}
|
2009-03-04 04:28:35 +01:00
|
|
|
entry->allocations = 1;
|
|
|
|
entry->slot = slot;
|
|
|
|
entry->prev = NULL;
|
|
|
|
entry->next = gHashTable.slots[slot];
|
|
|
|
entry->numEntries = numEntries;
|
|
|
|
entry->size = size;
|
|
|
|
|
2013-01-26 02:13:45 +01:00
|
|
|
memcpy(entry->backtrace, backtrace, numEntries * sizeof(uintptr_t));
|
2009-03-04 04:28:35 +01:00
|
|
|
|
|
|
|
gHashTable.slots[slot] = entry;
|
|
|
|
|
|
|
|
if (entry->next != NULL) {
|
|
|
|
entry->next->prev = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we just added an entry, increase the size of the hashtable
|
|
|
|
gHashTable.count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
static int is_valid_entry(HashEntry* entry) {
|
2009-03-04 04:28:35 +01:00
|
|
|
if (entry != NULL) {
|
|
|
|
int i;
|
|
|
|
for (i = 0 ; i < HASHTABLE_SIZE ; i++) {
|
|
|
|
HashEntry* e1 = gHashTable.slots[i];
|
|
|
|
|
|
|
|
while (e1 != NULL) {
|
|
|
|
if (e1 == entry) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
e1 = e1->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
static void remove_entry(HashEntry* entry) {
|
2009-03-04 04:28:35 +01:00
|
|
|
HashEntry* prev = entry->prev;
|
|
|
|
HashEntry* next = entry->next;
|
|
|
|
|
|
|
|
if (prev != NULL) entry->prev->next = next;
|
|
|
|
if (next != NULL) entry->next->prev = prev;
|
|
|
|
|
|
|
|
if (prev == NULL) {
|
|
|
|
// we are the head of the list. set the head to be next
|
|
|
|
gHashTable.slots[entry->slot] = entry->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we just removed and entry, decrease the size of the hashtable
|
|
|
|
gHashTable.count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// =============================================================================
|
bionic: import heaptracker as chk_malloc
This patch is a rewrite of libc.debug.malloc = 10 (chk_malloc). It provides
the same features as the original (poison freed memory, detect heap overruns
and underruns), except that it provides more debugging information whenever it
detects a problem.
In addition to the original features, the new chk_malloc() implementation
detects multiple frees within a given range of the last N allocations, N being
configurable via the system property libc.debug.malloc.backlog.
Finally, this patch keeps track of all outstanding memory allocations. On
program exit, we walk that list and report each outstanding allocation.
(There is support (not enabled) for a scanner thread periodically walks over
the list of outstanding allocations as well as the backlog of recently-freed
allocations, checking for heap-usage errors.)
Feature overview:
1) memory leaks
2) multiple frees
3) use after free
4) overrun
Implementation:
-- for each allocation, there is a:
1) stack trace at the time the allocation is made
2) if the memory is freed, there is also a stack trace at the point
3) a front and rear guard (fence)
4) the stack traces are kept together with the allocation
-- the following lists and maintained
1) all outstanding memory allocations
3) a backlog of allocations what are freed; when you call free(), instead of
actually freed, the allocation is moved to this backlog;
4) when the backlog of allocations gets full, the oldest entry gets evicted
from it; at that point, the allocation is checked for overruns or
use-after-free errors, and then actually freed.
5) when the program exits, the list of outstanding allocations and the
backlog are inspected for errors, then freed;
To use this, set the following system properties before running the process or
processes you want to inspect:
libc.malloc.debug.backlog # defaults to 100
libc.malloc.debug 10
When a problem is detected, you will see the following on logcat for a multiple
free:
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 BYTES MULTIPLY FREED!
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 4009647c /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 FIRST FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c7d2 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 40096490 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9278 SIZE 10 NOW BEING FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c6ac /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 400964a0 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
The following for a heap overrun and underrun:
E/libc ( 7233): +++ REAR GUARD MISMATCH [10, 11)
E/libc ( 7233): +++ ALLOCATION 0x404b9198 SIZE 10 HAS A CORRUPTED REAR GUARD
E/libc ( 7233): +++ ALLOCATION 0x404b9198 SIZE 10 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 40096438 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9198 SIZE 10 FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c7d2 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 40096462 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9358 SIZE 10 HAS A CORRUPTED FRONT GUARD
E/libc ( 7233): +++ ALLOCATION 0x404b9358 SIZE 10 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 400964ba /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
E/libc ( 7233): +++ ALLOCATION 0x404b9358 SIZE 10 FREED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c7d2 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d94 /system/lib/libc.so
E/libc ( 7233): #03 pc 400964e4 /system/bin/malloctest
E/libc ( 7233): #04 pc 00016f24 /system/lib/libc.so
The following for a memory leak:
E/libc ( 7233): +++ THERE ARE 1 LEAKED ALLOCATIONS
E/libc ( 7233): +++ DELETING 4096 BYTES OF LEAKED MEMORY AT 0x404b95e8 (1 REMAINING)
E/libc ( 7233): +++ ALLOCATION 0x404b95e8 SIZE 4096 ALLOCATED HERE:
E/libc ( 7233): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
E/libc ( 7233): #00 pc 0000c35a /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #01 pc 0000c658 /system/lib/libc_malloc_debug_leak.so
E/libc ( 7233): #02 pc 00016d80 /system/lib/libc.so
E/libc ( 7233): #03 pc 0001bc94 /system/lib/libc.so
E/libc ( 7233): #04 pc 0001edf6 /system/lib/libc.so
E/libc ( 7233): #05 pc 0001b80a /system/lib/libc.so
E/libc ( 7233): #06 pc 0001c086 /system/lib/libc.so
E/libc ( 7233): #07 pc 40096402 /system/bin/malloctest
E/libc ( 7233): #08 pc 00016f24 /system/lib/libc.so
Change-Id: Ic440e9d05a01e2ea86b25e8998714e88bc2d16e0
Signed-off-by: Iliyan Malchev <malchev@google.com>
2012-05-29 23:22:42 +02:00
|
|
|
// malloc fill functions
|
2009-03-04 04:28:35 +01:00
|
|
|
// =============================================================================
|
|
|
|
|
|
|
|
#define CHK_FILL_FREE 0xef
|
2012-08-28 23:15:04 +02:00
|
|
|
#define CHK_SENTINEL_VALUE 0xeb
|
|
|
|
|
|
|
|
extern "C" void* fill_calloc(size_t n_elements, size_t elem_size) {
|
|
|
|
return dlcalloc(n_elements, elem_size);
|
|
|
|
}
|
2009-03-04 04:28:35 +01:00
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void* fill_malloc(size_t bytes) {
|
2009-03-04 04:28:35 +01:00
|
|
|
void* buffer = dlmalloc(bytes);
|
|
|
|
if (buffer) {
|
|
|
|
memset(buffer, CHK_SENTINEL_VALUE, bytes);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void fill_free(void* mem) {
|
2009-03-04 04:28:35 +01:00
|
|
|
size_t bytes = dlmalloc_usable_size(mem);
|
|
|
|
memset(mem, CHK_FILL_FREE, bytes);
|
|
|
|
dlfree(mem);
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void* fill_realloc(void* mem, size_t bytes) {
|
2009-03-04 04:28:35 +01:00
|
|
|
void* buffer = fill_malloc(bytes);
|
|
|
|
if (mem == NULL) {
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
if (buffer) {
|
|
|
|
size_t old_size = dlmalloc_usable_size(mem);
|
|
|
|
size_t size = (bytes < old_size)?(bytes):(old_size);
|
|
|
|
memcpy(buffer, mem, size);
|
|
|
|
fill_free(mem);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void* fill_memalign(size_t alignment, size_t bytes) {
|
2009-03-04 04:28:35 +01:00
|
|
|
void* buffer = dlmemalign(alignment, bytes);
|
|
|
|
if (buffer) {
|
|
|
|
memset(buffer, CHK_SENTINEL_VALUE, bytes);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
// =============================================================================
|
|
|
|
// malloc leak functions
|
|
|
|
// =============================================================================
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
static void* MEMALIGN_GUARD = reinterpret_cast<void*>(0xA1A41520);
|
2009-03-04 04:28:35 +01:00
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void* leak_malloc(size_t bytes) {
|
2009-03-04 04:28:35 +01:00
|
|
|
// allocate enough space infront of the allocation to store the pointer for
|
|
|
|
// the alloc structure. This will making free'ing the structer really fast!
|
|
|
|
|
|
|
|
// 1. allocate enough memory and include our header
|
|
|
|
// 2. set the base pointer to be right after our header
|
|
|
|
|
2012-03-14 07:48:39 +01:00
|
|
|
size_t size = bytes + sizeof(AllocationEntry);
|
|
|
|
if (size < bytes) { // Overflow.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* base = dlmalloc(size);
|
2009-03-04 04:28:35 +01:00
|
|
|
if (base != NULL) {
|
2012-08-28 23:15:04 +02:00
|
|
|
ScopedPthreadMutexLocker locker(&gAllocationsMutex);
|
2009-03-04 04:28:35 +01:00
|
|
|
|
2013-01-26 02:13:45 +01:00
|
|
|
uintptr_t backtrace[BACKTRACE_SIZE];
|
2012-08-28 23:15:04 +02:00
|
|
|
size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
|
2009-11-17 23:13:38 +01:00
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
AllocationEntry* header = reinterpret_cast<AllocationEntry*>(base);
|
|
|
|
header->entry = record_backtrace(backtrace, numEntries, bytes);
|
|
|
|
header->guard = GUARD;
|
2009-11-17 23:13:38 +01:00
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
// now increment base to point to after our header.
|
|
|
|
// this should just work since our header is 8 bytes.
|
|
|
|
base = reinterpret_cast<AllocationEntry*>(base) + 1;
|
2009-03-04 04:28:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void leak_free(void* mem) {
|
2009-03-04 04:28:35 +01:00
|
|
|
if (mem != NULL) {
|
2012-08-28 23:15:04 +02:00
|
|
|
ScopedPthreadMutexLocker locker(&gAllocationsMutex);
|
2009-03-04 04:28:35 +01:00
|
|
|
|
|
|
|
// check the guard to make sure it is valid
|
2012-08-28 23:15:04 +02:00
|
|
|
AllocationEntry* header = to_header(mem);
|
2009-11-17 23:13:38 +01:00
|
|
|
|
2009-03-04 04:28:35 +01:00
|
|
|
if (header->guard != GUARD) {
|
|
|
|
// could be a memaligned block
|
2012-08-28 23:15:04 +02:00
|
|
|
if (reinterpret_cast<void**>(mem)[-1] == MEMALIGN_GUARD) {
|
|
|
|
mem = reinterpret_cast<void**>(mem)[-2];
|
|
|
|
header = to_header(mem);
|
2009-03-04 04:28:35 +01:00
|
|
|
}
|
|
|
|
}
|
2009-11-17 23:13:38 +01:00
|
|
|
|
2009-03-04 04:28:35 +01:00
|
|
|
if (header->guard == GUARD || is_valid_entry(header->entry)) {
|
|
|
|
// decrement the allocations
|
|
|
|
HashEntry* entry = header->entry;
|
|
|
|
entry->allocations--;
|
|
|
|
if (entry->allocations <= 0) {
|
|
|
|
remove_entry(entry);
|
|
|
|
dlfree(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now free the memory!
|
|
|
|
dlfree(header);
|
|
|
|
} else {
|
|
|
|
debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
|
|
|
|
header->guard, header->entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
|
2009-03-04 04:28:35 +01:00
|
|
|
/* 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 && MAX_SIZE_T / n_elements < elem_size) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-08-28 23:15:04 +02:00
|
|
|
size_t size = n_elements * elem_size;
|
|
|
|
void* ptr = leak_malloc(size);
|
2009-03-04 04:28:35 +01:00
|
|
|
if (ptr != NULL) {
|
|
|
|
memset(ptr, 0, size);
|
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
|
2009-03-04 04:28:35 +01:00
|
|
|
if (oldMem == NULL) {
|
|
|
|
return leak_malloc(bytes);
|
|
|
|
}
|
|
|
|
void* newMem = NULL;
|
2012-08-28 23:15:04 +02:00
|
|
|
AllocationEntry* header = to_header(oldMem);
|
2009-03-04 04:28:35 +01:00
|
|
|
if (header && header->guard == GUARD) {
|
|
|
|
size_t oldSize = header->entry->size & ~SIZE_FLAG_MASK;
|
|
|
|
newMem = leak_malloc(bytes);
|
|
|
|
if (newMem != NULL) {
|
|
|
|
size_t copySize = (oldSize <= bytes) ? oldSize : bytes;
|
|
|
|
memcpy(newMem, oldMem, copySize);
|
|
|
|
leak_free(oldMem);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newMem = dlrealloc(oldMem, bytes);
|
|
|
|
}
|
|
|
|
return newMem;
|
|
|
|
}
|
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
extern "C" void* leak_memalign(size_t alignment, size_t bytes) {
|
2009-03-04 04:28:35 +01:00
|
|
|
// we can just use malloc
|
2012-08-28 23:15:04 +02:00
|
|
|
if (alignment <= MALLOC_ALIGNMENT) {
|
2009-03-04 04:28:35 +01:00
|
|
|
return leak_malloc(bytes);
|
2012-08-28 23:15:04 +02:00
|
|
|
}
|
2009-03-04 04:28:35 +01:00
|
|
|
|
|
|
|
// need to make sure it's a power of two
|
2012-08-28 23:15:04 +02:00
|
|
|
if (alignment & (alignment-1)) {
|
2009-03-04 04:28:35 +01:00
|
|
|
alignment = 1L << (31 - __builtin_clz(alignment));
|
2012-08-28 23:15:04 +02:00
|
|
|
}
|
2009-11-17 23:13:38 +01:00
|
|
|
|
2012-10-10 02:23:09 +02:00
|
|
|
// here, alignment is at least MALLOC_ALIGNMENT<<1 bytes
|
2009-03-04 04:28:35 +01:00
|
|
|
// we will align by at least MALLOC_ALIGNMENT bytes
|
|
|
|
// and at most alignment-MALLOC_ALIGNMENT bytes
|
|
|
|
size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;
|
2012-03-14 07:48:39 +01:00
|
|
|
if (size < bytes) { // Overflow.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-04 04:28:35 +01:00
|
|
|
void* base = leak_malloc(size);
|
|
|
|
if (base != NULL) {
|
2012-08-28 23:15:04 +02:00
|
|
|
intptr_t ptr = reinterpret_cast<intptr_t>(base);
|
|
|
|
if ((ptr % alignment) == 0) {
|
2009-03-04 04:28:35 +01:00
|
|
|
return base;
|
2012-08-28 23:15:04 +02:00
|
|
|
}
|
2009-03-04 04:28:35 +01:00
|
|
|
|
|
|
|
// align the pointer
|
|
|
|
ptr += ((-ptr) % alignment);
|
2009-11-17 23:13:38 +01:00
|
|
|
|
2009-03-04 04:28:35 +01:00
|
|
|
// there is always enough space for the base pointer and the guard
|
2012-08-28 23:15:04 +02:00
|
|
|
reinterpret_cast<void**>(ptr)[-1] = MEMALIGN_GUARD;
|
|
|
|
reinterpret_cast<void**>(ptr)[-2] = base;
|
2009-03-04 04:28:35 +01:00
|
|
|
|
2012-08-28 23:15:04 +02:00
|
|
|
return reinterpret_cast<void*>(ptr);
|
2009-03-04 04:28:35 +01:00
|
|
|
}
|
|
|
|
return base;
|
|
|
|
}
|