mirror of
https://github.com/frida/tinycc
synced 2025-01-23 03:32:03 +03:00
Add bound checking print heap
This commit is contained in:
parent
fb22e0c12d
commit
4d297d354c
154
lib/bcheck.c
154
lib/bcheck.c
@ -77,8 +77,16 @@ static void *(*calloc_redir) (size_t, size_t) = NULL;
|
||||
static void (*free_redir) (void *) = NULL;
|
||||
static void *(*realloc_redir) (void *, size_t) = NULL;
|
||||
static void *(*memalign_redir) (size_t, size_t) = NULL;
|
||||
static int pool_index = 0;
|
||||
static unsigned char initial_pool[256];
|
||||
#endif
|
||||
|
||||
#define TCC_TYPE_NONE (0)
|
||||
#define TCC_TYPE_MALLOC (1)
|
||||
#define TCC_TYPE_CALLOC (2)
|
||||
#define TCC_TYPE_REALLOC (3)
|
||||
#define TCC_TYPE_MEMALIGN (4)
|
||||
|
||||
/* this pointer is generated when bound check is incorrect */
|
||||
#define INVALID_POINTER ((void *)(-2))
|
||||
|
||||
@ -87,6 +95,7 @@ struct tree_node {
|
||||
Tree * left, * right;
|
||||
size_t start;
|
||||
size_t size;
|
||||
size_t type;
|
||||
size_t is_invalid; /* true if pointers outside region are invalid */
|
||||
};
|
||||
|
||||
@ -104,6 +113,7 @@ void splay_printtree(Tree * t, int d);
|
||||
|
||||
/* external interface */
|
||||
void __bound_init(void);
|
||||
void __bound_exit(void);
|
||||
|
||||
#ifdef __attribute__
|
||||
/* an __attribute__ macro is defined in the system headers */
|
||||
@ -138,6 +148,7 @@ static alloca_list_type *alloca_list = NULL;
|
||||
|
||||
static int inited = 0;
|
||||
static int print_calls = 0;
|
||||
static int print_heap = 0;
|
||||
static int never_fatal = 0;
|
||||
static int no_checking = 0;
|
||||
|
||||
@ -281,10 +292,10 @@ void FASTCALL __bound_local_new(void *p1)
|
||||
addr += fp;
|
||||
size = p[1];
|
||||
dprintf(stderr, "%s, %s() (%p 0x%lx)\n",
|
||||
__FILE__, __FUNCTION__, addr, (unsigned long) size);
|
||||
__FILE__, __FUNCTION__, (void *) addr, (unsigned long) size);
|
||||
tree = splay_insert(addr, size, tree);
|
||||
}
|
||||
p += 2;
|
||||
tree = splay_insert(addr, size, tree);
|
||||
}
|
||||
POST_SEM ();
|
||||
}
|
||||
@ -306,9 +317,10 @@ void FASTCALL __bound_local_delete(void *p1)
|
||||
break;
|
||||
if (addr == 1) {
|
||||
while (alloca_list && alloca_list->fp == fp) {
|
||||
alloca_list_type *next = alloca_list->next;
|
||||
|
||||
dprintf(stderr, "%s, %s() remove alloca/vla %p\n",
|
||||
__FILE__, __FUNCTION__, alloca_list->p);
|
||||
alloca_list_type *next = alloca_list->next;
|
||||
tree = splay_delete ((size_t) alloca_list->p, tree);
|
||||
#if MALLOC_REDIR
|
||||
free_redir (alloca_list);
|
||||
@ -322,17 +334,13 @@ void FASTCALL __bound_local_delete(void *p1)
|
||||
addr += fp;
|
||||
dprintf(stderr, "%s, %s() (%p 0x%lx)\n",
|
||||
__FILE__, __FUNCTION__, (void *) addr, (unsigned long) p[1]);
|
||||
tree = splay_delete(addr, tree);
|
||||
}
|
||||
p += 2;
|
||||
tree = splay_delete(addr, tree);
|
||||
}
|
||||
POST_SEM ();
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 6)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
void __bound_init(void)
|
||||
{
|
||||
if (inited)
|
||||
@ -341,6 +349,7 @@ void __bound_init(void)
|
||||
inited = 1;
|
||||
|
||||
print_calls = getenv ("TCC_BOUNDS_PRINT_CALLS") != NULL;
|
||||
print_heap = getenv ("TCC_BOUNDS_PRINT_HEAP") != NULL;
|
||||
never_fatal = getenv ("TCC_BOUNDS_NEVER_FATAL") != NULL;
|
||||
|
||||
dprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__);
|
||||
@ -431,14 +440,23 @@ void __bound_main_arg(char **p)
|
||||
POST_SEM ();
|
||||
}
|
||||
|
||||
void __bound_exit(void)
|
||||
void __attribute__((destructor)) __bound_exit(void)
|
||||
{
|
||||
int i;
|
||||
static const char * const alloc_type[] = {
|
||||
"", "malloc", "calloc", "realloc", "memalign"
|
||||
};
|
||||
|
||||
dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__);
|
||||
while (tree) {
|
||||
tree = splay_delete (tree->start, tree);
|
||||
|
||||
if (inited) {
|
||||
#if !defined(_WIN32)
|
||||
if (print_heap) {
|
||||
extern void __libc_freeres ();
|
||||
__libc_freeres ();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TREE_REUSE
|
||||
while (tree_free_list) {
|
||||
Tree *next = tree_free_list->left;
|
||||
@ -451,14 +469,26 @@ void __bound_exit(void)
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < FREE_REUSE_SIZE; i++) {
|
||||
if (free_reuse_list[i]) {
|
||||
tree = splay_delete ((size_t) free_reuse_list[i], tree);
|
||||
#if MALLOC_REDIR
|
||||
free_redir (free_reuse_list[i]);
|
||||
#else
|
||||
free (free_reuse_list[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
while (tree) {
|
||||
if (print_heap && tree->type != 0) {
|
||||
fprintf (stderr, "%s, %s() %s found size %ld\n",
|
||||
__FILE__, __FUNCTION__, alloc_type[tree->type],
|
||||
(unsigned long) tree->size);
|
||||
}
|
||||
tree = splay_delete (tree->start, tree);
|
||||
}
|
||||
EXIT_SEM ();
|
||||
inited = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: we should use a malloc which ensure that it is unlikely that
|
||||
@ -475,15 +505,11 @@ void *__bound_malloc(size_t size, const void *caller)
|
||||
#if MALLOC_REDIR
|
||||
/* This will catch the first dlsym call from __bound_init */
|
||||
if (malloc_redir == NULL) {
|
||||
static int pool_index = 0;
|
||||
static unsigned char pool[256];
|
||||
void *retval;
|
||||
|
||||
retval = &pool[pool_index];
|
||||
ptr = &initial_pool[pool_index];
|
||||
pool_index = (pool_index + size + 7) & ~8;
|
||||
dprintf (stderr, "%s, %s initial (%p, 0x%x)\n",
|
||||
__FILE__, __FUNCTION__, retval, (unsigned)size);
|
||||
return retval;
|
||||
__FILE__, __FUNCTION__, ptr, (unsigned)size);
|
||||
return ptr;
|
||||
}
|
||||
#endif
|
||||
/* we allocate one more byte to ensure the regions will be
|
||||
@ -501,6 +527,9 @@ void *__bound_malloc(size_t size, const void *caller)
|
||||
|
||||
if (ptr) {
|
||||
tree = splay_insert ((size_t) ptr, size, tree);
|
||||
if (tree->start == (size_t) ptr) {
|
||||
tree->type = TCC_TYPE_MALLOC;
|
||||
}
|
||||
}
|
||||
POST_SEM ();
|
||||
return ptr;
|
||||
@ -542,6 +571,9 @@ void *__bound_memalign(size_t size, size_t align, const void *caller)
|
||||
dprintf(stderr, "%s, %s (%p, 0x%x)\n",
|
||||
__FILE__, __FUNCTION__, ptr, (unsigned)size);
|
||||
tree = splay_insert((size_t) ptr, size, tree);
|
||||
if (tree->start == (size_t) ptr) {
|
||||
tree->type = TCC_TYPE_MEMALIGN;
|
||||
}
|
||||
}
|
||||
POST_SEM ();
|
||||
return ptr;
|
||||
@ -556,7 +588,12 @@ void __bound_free(void *ptr, const void *caller)
|
||||
size_t addr = (size_t) ptr;
|
||||
void *p;
|
||||
|
||||
if (ptr == NULL || tree == NULL)
|
||||
if (ptr == NULL || tree == NULL
|
||||
#if MALLOC_REDIR
|
||||
|| ((unsigned char *) ptr >= &initial_pool[0] &&
|
||||
(unsigned char *) ptr < &initial_pool[sizeof(initial_pool)])
|
||||
#endif
|
||||
)
|
||||
return;
|
||||
|
||||
dprintf(stderr, "%s, %s (%p)\n", __FILE__, __FUNCTION__, ptr);
|
||||
@ -593,9 +630,6 @@ void *realloc(void *ptr, size_t size)
|
||||
void *__bound_realloc(void *ptr, size_t size, const void *caller)
|
||||
#endif
|
||||
{
|
||||
void *ptr1;
|
||||
size_t last_size;
|
||||
|
||||
if (size == 0) {
|
||||
#if MALLOC_REDIR
|
||||
free(ptr);
|
||||
@ -603,10 +637,26 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller)
|
||||
__bound_free(ptr, caller);
|
||||
#endif
|
||||
return NULL;
|
||||
} else {
|
||||
}
|
||||
else if (ptr == NULL) {
|
||||
#if MALLOC_REDIR
|
||||
ptr = realloc_redir (ptr, size);
|
||||
#else
|
||||
ptr = realloc (ptr, size);
|
||||
#endif
|
||||
WAIT_SEM ();
|
||||
tree = splay ((size_t) ptr, tree);
|
||||
if (tree->start != (size_t) ptr) {
|
||||
if (ptr) {
|
||||
tree = splay_insert ((size_t) ptr, size, tree);
|
||||
if (tree->start == (size_t) ptr) {
|
||||
tree->type = TCC_TYPE_REALLOC;
|
||||
}
|
||||
}
|
||||
POST_SEM ();
|
||||
return ptr;
|
||||
}
|
||||
else {
|
||||
WAIT_SEM ();
|
||||
tree = splay_delete ((size_t) ptr, tree);
|
||||
#if MALLOC_REDIR
|
||||
ptr = realloc_redir (ptr, size);
|
||||
#else
|
||||
@ -614,29 +664,13 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller)
|
||||
#endif
|
||||
if (ptr) {
|
||||
tree = splay_insert ((size_t) ptr, size, tree);
|
||||
if (tree->start == (size_t) ptr) {
|
||||
tree->type = TCC_TYPE_REALLOC;
|
||||
}
|
||||
}
|
||||
POST_SEM ();
|
||||
return ptr;
|
||||
}
|
||||
else {
|
||||
last_size = tree->size;
|
||||
POST_SEM ();
|
||||
#if MALLOC_REDIR
|
||||
ptr1 = malloc(size);
|
||||
#else
|
||||
ptr1 = __bound_malloc(size, caller);
|
||||
#endif
|
||||
if (ptr == NULL || ptr1 == NULL)
|
||||
return ptr1;
|
||||
memcpy(ptr1, ptr, last_size < size ? last_size : size);
|
||||
#if MALLOC_REDIR
|
||||
free(ptr);
|
||||
#else
|
||||
__bound_free(ptr, caller);
|
||||
#endif
|
||||
return ptr1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if MALLOC_REDIR
|
||||
@ -649,12 +683,29 @@ void *__bound_calloc(size_t nmemb, size_t size)
|
||||
|
||||
size *= nmemb;
|
||||
#if MALLOC_REDIR
|
||||
ptr = malloc(size);
|
||||
/* This will catch the first dlsym call from __bound_init */
|
||||
if (malloc_redir == NULL) {
|
||||
ptr = &initial_pool[pool_index];
|
||||
pool_index = (pool_index + size + 7) & ~8;
|
||||
dprintf (stderr, "%s, %s initial (%p, 0x%x)\n",
|
||||
__FILE__, __FUNCTION__, ptr, (unsigned)size);
|
||||
memset (ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
#endif
|
||||
#if MALLOC_REDIR
|
||||
ptr = malloc_redir(size + 1);
|
||||
#else
|
||||
ptr = __bound_malloc(size, NULL);
|
||||
ptr = malloc(size + 1);
|
||||
#endif
|
||||
if (ptr) {
|
||||
memset (ptr, 0, size);
|
||||
WAIT_SEM ();
|
||||
tree = splay_insert ((size_t) ptr, size, tree);
|
||||
if (tree->start == (size_t) ptr) {
|
||||
tree->type = TCC_TYPE_CALLOC;
|
||||
}
|
||||
POST_SEM ();
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
@ -700,6 +751,7 @@ void __bound_new_region(void *p, size_t size)
|
||||
dprintf(stderr, "%s, %s (%p, 0x%x)\n",
|
||||
__FILE__, __FUNCTION__, p, (unsigned)size);
|
||||
WAIT_SEM ();
|
||||
GET_CALLER_FP (fp);
|
||||
last = NULL;
|
||||
cur = alloca_list;
|
||||
while (cur && cur->fp == fp) {
|
||||
@ -710,7 +762,7 @@ void __bound_new_region(void *p, size_t size)
|
||||
last->next = cur->next;
|
||||
}
|
||||
else {
|
||||
alloca_list->next = cur->next;
|
||||
alloca_list = cur->next;
|
||||
}
|
||||
tree = splay_delete((size_t)p, tree);
|
||||
#if MALLOC_REDIR
|
||||
@ -718,6 +770,7 @@ void __bound_new_region(void *p, size_t size)
|
||||
#else
|
||||
free (cur);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
last = cur;
|
||||
cur = cur->next;
|
||||
@ -729,7 +782,6 @@ void __bound_new_region(void *p, size_t size)
|
||||
cur = malloc (sizeof (alloca_list_type));
|
||||
#endif
|
||||
if (cur) {
|
||||
GET_CALLER_FP (fp);
|
||||
cur->fp = fp;
|
||||
cur->p = p;
|
||||
cur->next = alloca_list;
|
||||
@ -738,6 +790,11 @@ void __bound_new_region(void *p, size_t size)
|
||||
POST_SEM ();
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 6)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
/* some useful checked functions */
|
||||
|
||||
/* check that (p ... p + size - 1) lies inside 'p' region, if any */
|
||||
@ -1005,6 +1062,7 @@ static Tree * splay_insert(size_t addr, size_t size, Tree * t)
|
||||
}
|
||||
new->start = addr;
|
||||
new->size = size;
|
||||
new->type = TCC_TYPE_NONE;
|
||||
new->is_invalid = 0;
|
||||
return new;
|
||||
}
|
||||
|
13
tcc-doc.texi
13
tcc-doc.texi
@ -354,8 +354,19 @@ fault}.
|
||||
Generate additional support code to check
|
||||
memory allocations and array/pointer bounds. @option{-g} is implied. Note
|
||||
that the generated code is slower and bigger in this case.
|
||||
The bound checking code is not included in shared libaries. The main executable should always be compiled with the @option{-b}.
|
||||
|
||||
Note: @option{-b} is only available on i386 when using libtcc for the moment.
|
||||
There are three environment variables that can be used:
|
||||
@table @option
|
||||
@item TCC_BOUNDS_PRINT_CALLS
|
||||
Print bound checking calls. Can be used for debugging.
|
||||
@item TCC_BOUNDS_PRINT_HEAP
|
||||
Print heap objects that are not freed at exit of program.
|
||||
@item TCC_BOUNDS_NEVER_FATAL
|
||||
Try to continue in case of a bound checking error.
|
||||
@end table
|
||||
|
||||
Note: @option{-b} is only available on i386 (linux and windows) and x86_64 (linux and windows) when using libtcc for the moment.
|
||||
|
||||
@item -bt N
|
||||
Display N callers in stack traces. This is useful with @option{-g} or
|
||||
|
Loading…
Reference in New Issue
Block a user