mirror of
https://github.com/mirror/tinycc.git
synced 2025-02-14 07:10:07 +08:00
Add bound checking print heap
This commit is contained in:
parent
fb22e0c12d
commit
4d297d354c
186
lib/bcheck.c
186
lib/bcheck.c
@ -77,8 +77,16 @@ static void *(*calloc_redir) (size_t, size_t) = NULL;
|
|||||||
static void (*free_redir) (void *) = NULL;
|
static void (*free_redir) (void *) = NULL;
|
||||||
static void *(*realloc_redir) (void *, size_t) = NULL;
|
static void *(*realloc_redir) (void *, size_t) = NULL;
|
||||||
static void *(*memalign_redir) (size_t, size_t) = NULL;
|
static void *(*memalign_redir) (size_t, size_t) = NULL;
|
||||||
|
static int pool_index = 0;
|
||||||
|
static unsigned char initial_pool[256];
|
||||||
#endif
|
#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 */
|
/* this pointer is generated when bound check is incorrect */
|
||||||
#define INVALID_POINTER ((void *)(-2))
|
#define INVALID_POINTER ((void *)(-2))
|
||||||
|
|
||||||
@ -87,6 +95,7 @@ struct tree_node {
|
|||||||
Tree * left, * right;
|
Tree * left, * right;
|
||||||
size_t start;
|
size_t start;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
size_t type;
|
||||||
size_t is_invalid; /* true if pointers outside region are invalid */
|
size_t is_invalid; /* true if pointers outside region are invalid */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -104,6 +113,7 @@ void splay_printtree(Tree * t, int d);
|
|||||||
|
|
||||||
/* external interface */
|
/* external interface */
|
||||||
void __bound_init(void);
|
void __bound_init(void);
|
||||||
|
void __bound_exit(void);
|
||||||
|
|
||||||
#ifdef __attribute__
|
#ifdef __attribute__
|
||||||
/* an __attribute__ macro is defined in the system headers */
|
/* 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 inited = 0;
|
||||||
static int print_calls = 0;
|
static int print_calls = 0;
|
||||||
|
static int print_heap = 0;
|
||||||
static int never_fatal = 0;
|
static int never_fatal = 0;
|
||||||
static int no_checking = 0;
|
static int no_checking = 0;
|
||||||
|
|
||||||
@ -281,10 +292,10 @@ void FASTCALL __bound_local_new(void *p1)
|
|||||||
addr += fp;
|
addr += fp;
|
||||||
size = p[1];
|
size = p[1];
|
||||||
dprintf(stderr, "%s, %s() (%p 0x%lx)\n",
|
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;
|
p += 2;
|
||||||
tree = splay_insert(addr, size, tree);
|
|
||||||
}
|
}
|
||||||
POST_SEM ();
|
POST_SEM ();
|
||||||
}
|
}
|
||||||
@ -306,9 +317,10 @@ void FASTCALL __bound_local_delete(void *p1)
|
|||||||
break;
|
break;
|
||||||
if (addr == 1) {
|
if (addr == 1) {
|
||||||
while (alloca_list && alloca_list->fp == fp) {
|
while (alloca_list && alloca_list->fp == fp) {
|
||||||
|
alloca_list_type *next = alloca_list->next;
|
||||||
|
|
||||||
dprintf(stderr, "%s, %s() remove alloca/vla %p\n",
|
dprintf(stderr, "%s, %s() remove alloca/vla %p\n",
|
||||||
__FILE__, __FUNCTION__, alloca_list->p);
|
__FILE__, __FUNCTION__, alloca_list->p);
|
||||||
alloca_list_type *next = alloca_list->next;
|
|
||||||
tree = splay_delete ((size_t) alloca_list->p, tree);
|
tree = splay_delete ((size_t) alloca_list->p, tree);
|
||||||
#if MALLOC_REDIR
|
#if MALLOC_REDIR
|
||||||
free_redir (alloca_list);
|
free_redir (alloca_list);
|
||||||
@ -322,17 +334,13 @@ void FASTCALL __bound_local_delete(void *p1)
|
|||||||
addr += fp;
|
addr += fp;
|
||||||
dprintf(stderr, "%s, %s() (%p 0x%lx)\n",
|
dprintf(stderr, "%s, %s() (%p 0x%lx)\n",
|
||||||
__FILE__, __FUNCTION__, (void *) addr, (unsigned long) p[1]);
|
__FILE__, __FUNCTION__, (void *) addr, (unsigned long) p[1]);
|
||||||
|
tree = splay_delete(addr, tree);
|
||||||
}
|
}
|
||||||
p += 2;
|
p += 2;
|
||||||
tree = splay_delete(addr, tree);
|
|
||||||
}
|
}
|
||||||
POST_SEM ();
|
POST_SEM ();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__GNUC__) && (__GNUC__ >= 6)
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void __bound_init(void)
|
void __bound_init(void)
|
||||||
{
|
{
|
||||||
if (inited)
|
if (inited)
|
||||||
@ -341,6 +349,7 @@ void __bound_init(void)
|
|||||||
inited = 1;
|
inited = 1;
|
||||||
|
|
||||||
print_calls = getenv ("TCC_BOUNDS_PRINT_CALLS") != NULL;
|
print_calls = getenv ("TCC_BOUNDS_PRINT_CALLS") != NULL;
|
||||||
|
print_heap = getenv ("TCC_BOUNDS_PRINT_HEAP") != NULL;
|
||||||
never_fatal = getenv ("TCC_BOUNDS_NEVER_FATAL") != NULL;
|
never_fatal = getenv ("TCC_BOUNDS_NEVER_FATAL") != NULL;
|
||||||
|
|
||||||
dprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__);
|
dprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__);
|
||||||
@ -431,34 +440,55 @@ void __bound_main_arg(char **p)
|
|||||||
POST_SEM ();
|
POST_SEM ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void __bound_exit(void)
|
void __attribute__((destructor)) __bound_exit(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
static const char * const alloc_type[] = {
|
||||||
|
"", "malloc", "calloc", "realloc", "memalign"
|
||||||
|
};
|
||||||
|
|
||||||
dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__);
|
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
|
#if TREE_REUSE
|
||||||
while (tree_free_list) {
|
while (tree_free_list) {
|
||||||
Tree *next = tree_free_list->left;
|
Tree *next = tree_free_list->left;
|
||||||
#if MALLOC_REDIR
|
#if MALLOC_REDIR
|
||||||
free_redir (tree_free_list);
|
free_redir (tree_free_list);
|
||||||
#else
|
#else
|
||||||
free (tree_free_list);
|
free (tree_free_list);
|
||||||
#endif
|
#endif
|
||||||
tree_free_list = next;
|
tree_free_list = next;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
for (i = 0; i < FREE_REUSE_SIZE; i++) {
|
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
|
#if MALLOC_REDIR
|
||||||
free_redir (free_reuse_list[i]);
|
free_redir (free_reuse_list[i]);
|
||||||
#else
|
#else
|
||||||
free (free_reuse_list[i]);
|
free (free_reuse_list[i]);
|
||||||
#endif
|
#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;
|
||||||
}
|
}
|
||||||
EXIT_SEM ();
|
|
||||||
inited = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: we should use a malloc which ensure that it is unlikely that
|
/* 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
|
#if MALLOC_REDIR
|
||||||
/* This will catch the first dlsym call from __bound_init */
|
/* This will catch the first dlsym call from __bound_init */
|
||||||
if (malloc_redir == NULL) {
|
if (malloc_redir == NULL) {
|
||||||
static int pool_index = 0;
|
ptr = &initial_pool[pool_index];
|
||||||
static unsigned char pool[256];
|
|
||||||
void *retval;
|
|
||||||
|
|
||||||
retval = &pool[pool_index];
|
|
||||||
pool_index = (pool_index + size + 7) & ~8;
|
pool_index = (pool_index + size + 7) & ~8;
|
||||||
dprintf (stderr, "%s, %s initial (%p, 0x%x)\n",
|
dprintf (stderr, "%s, %s initial (%p, 0x%x)\n",
|
||||||
__FILE__, __FUNCTION__, retval, (unsigned)size);
|
__FILE__, __FUNCTION__, ptr, (unsigned)size);
|
||||||
return retval;
|
return ptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* we allocate one more byte to ensure the regions will be
|
/* 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) {
|
if (ptr) {
|
||||||
tree = splay_insert ((size_t) ptr, size, tree);
|
tree = splay_insert ((size_t) ptr, size, tree);
|
||||||
|
if (tree->start == (size_t) ptr) {
|
||||||
|
tree->type = TCC_TYPE_MALLOC;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
POST_SEM ();
|
POST_SEM ();
|
||||||
return ptr;
|
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",
|
dprintf(stderr, "%s, %s (%p, 0x%x)\n",
|
||||||
__FILE__, __FUNCTION__, ptr, (unsigned)size);
|
__FILE__, __FUNCTION__, ptr, (unsigned)size);
|
||||||
tree = splay_insert((size_t) ptr, size, tree);
|
tree = splay_insert((size_t) ptr, size, tree);
|
||||||
|
if (tree->start == (size_t) ptr) {
|
||||||
|
tree->type = TCC_TYPE_MEMALIGN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
POST_SEM ();
|
POST_SEM ();
|
||||||
return ptr;
|
return ptr;
|
||||||
@ -556,7 +588,12 @@ void __bound_free(void *ptr, const void *caller)
|
|||||||
size_t addr = (size_t) ptr;
|
size_t addr = (size_t) ptr;
|
||||||
void *p;
|
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;
|
return;
|
||||||
|
|
||||||
dprintf(stderr, "%s, %s (%p)\n", __FILE__, __FUNCTION__, ptr);
|
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)
|
void *__bound_realloc(void *ptr, size_t size, const void *caller)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
void *ptr1;
|
|
||||||
size_t last_size;
|
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
#if MALLOC_REDIR
|
#if MALLOC_REDIR
|
||||||
free(ptr);
|
free(ptr);
|
||||||
@ -603,39 +637,39 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller)
|
|||||||
__bound_free(ptr, caller);
|
__bound_free(ptr, caller);
|
||||||
#endif
|
#endif
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
}
|
||||||
|
else if (ptr == NULL) {
|
||||||
|
#if MALLOC_REDIR
|
||||||
|
ptr = realloc_redir (ptr, size);
|
||||||
|
#else
|
||||||
|
ptr = realloc (ptr, size);
|
||||||
|
#endif
|
||||||
WAIT_SEM ();
|
WAIT_SEM ();
|
||||||
tree = splay ((size_t) ptr, tree);
|
if (ptr) {
|
||||||
if (tree->start != (size_t) ptr) {
|
tree = splay_insert ((size_t) ptr, size, tree);
|
||||||
#if MALLOC_REDIR
|
if (tree->start == (size_t) ptr) {
|
||||||
ptr = realloc_redir (ptr, size);
|
tree->type = TCC_TYPE_REALLOC;
|
||||||
#else
|
|
||||||
ptr = realloc (ptr, size);
|
|
||||||
#endif
|
|
||||||
if (ptr) {
|
|
||||||
tree = splay_insert ((size_t) ptr, size, tree);
|
|
||||||
}
|
}
|
||||||
POST_SEM ();
|
|
||||||
return ptr;
|
|
||||||
}
|
}
|
||||||
else {
|
POST_SEM ();
|
||||||
last_size = tree->size;
|
return ptr;
|
||||||
POST_SEM ();
|
}
|
||||||
|
else {
|
||||||
|
WAIT_SEM ();
|
||||||
|
tree = splay_delete ((size_t) ptr, tree);
|
||||||
#if MALLOC_REDIR
|
#if MALLOC_REDIR
|
||||||
ptr1 = malloc(size);
|
ptr = realloc_redir (ptr, size);
|
||||||
#else
|
#else
|
||||||
ptr1 = __bound_malloc(size, caller);
|
ptr = realloc (ptr, size);
|
||||||
#endif
|
#endif
|
||||||
if (ptr == NULL || ptr1 == NULL)
|
if (ptr) {
|
||||||
return ptr1;
|
tree = splay_insert ((size_t) ptr, size, tree);
|
||||||
memcpy(ptr1, ptr, last_size < size ? last_size : size);
|
if (tree->start == (size_t) ptr) {
|
||||||
#if MALLOC_REDIR
|
tree->type = TCC_TYPE_REALLOC;
|
||||||
free(ptr);
|
}
|
||||||
#else
|
|
||||||
__bound_free(ptr, caller);
|
|
||||||
#endif
|
|
||||||
return ptr1;
|
|
||||||
}
|
}
|
||||||
|
POST_SEM ();
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,12 +683,29 @@ void *__bound_calloc(size_t nmemb, size_t size)
|
|||||||
|
|
||||||
size *= nmemb;
|
size *= nmemb;
|
||||||
#if MALLOC_REDIR
|
#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
|
#else
|
||||||
ptr = __bound_malloc(size, NULL);
|
ptr = malloc(size + 1);
|
||||||
#endif
|
#endif
|
||||||
if (ptr) {
|
if (ptr) {
|
||||||
memset (ptr, 0, size);
|
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;
|
return ptr;
|
||||||
}
|
}
|
||||||
@ -700,6 +751,7 @@ void __bound_new_region(void *p, size_t size)
|
|||||||
dprintf(stderr, "%s, %s (%p, 0x%x)\n",
|
dprintf(stderr, "%s, %s (%p, 0x%x)\n",
|
||||||
__FILE__, __FUNCTION__, p, (unsigned)size);
|
__FILE__, __FUNCTION__, p, (unsigned)size);
|
||||||
WAIT_SEM ();
|
WAIT_SEM ();
|
||||||
|
GET_CALLER_FP (fp);
|
||||||
last = NULL;
|
last = NULL;
|
||||||
cur = alloca_list;
|
cur = alloca_list;
|
||||||
while (cur && cur->fp == fp) {
|
while (cur && cur->fp == fp) {
|
||||||
@ -710,7 +762,7 @@ void __bound_new_region(void *p, size_t size)
|
|||||||
last->next = cur->next;
|
last->next = cur->next;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
alloca_list->next = cur->next;
|
alloca_list = cur->next;
|
||||||
}
|
}
|
||||||
tree = splay_delete((size_t)p, tree);
|
tree = splay_delete((size_t)p, tree);
|
||||||
#if MALLOC_REDIR
|
#if MALLOC_REDIR
|
||||||
@ -718,6 +770,7 @@ void __bound_new_region(void *p, size_t size)
|
|||||||
#else
|
#else
|
||||||
free (cur);
|
free (cur);
|
||||||
#endif
|
#endif
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
last = cur;
|
last = cur;
|
||||||
cur = cur->next;
|
cur = cur->next;
|
||||||
@ -729,7 +782,6 @@ void __bound_new_region(void *p, size_t size)
|
|||||||
cur = malloc (sizeof (alloca_list_type));
|
cur = malloc (sizeof (alloca_list_type));
|
||||||
#endif
|
#endif
|
||||||
if (cur) {
|
if (cur) {
|
||||||
GET_CALLER_FP (fp);
|
|
||||||
cur->fp = fp;
|
cur->fp = fp;
|
||||||
cur->p = p;
|
cur->p = p;
|
||||||
cur->next = alloca_list;
|
cur->next = alloca_list;
|
||||||
@ -738,6 +790,11 @@ void __bound_new_region(void *p, size_t size)
|
|||||||
POST_SEM ();
|
POST_SEM ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && (__GNUC__ >= 6)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* some useful checked functions */
|
/* some useful checked functions */
|
||||||
|
|
||||||
/* check that (p ... p + size - 1) lies inside 'p' region, if any */
|
/* 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->start = addr;
|
||||||
new->size = size;
|
new->size = size;
|
||||||
|
new->type = TCC_TYPE_NONE;
|
||||||
new->is_invalid = 0;
|
new->is_invalid = 0;
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
13
tcc-doc.texi
13
tcc-doc.texi
@ -354,8 +354,19 @@ fault}.
|
|||||||
Generate additional support code to check
|
Generate additional support code to check
|
||||||
memory allocations and array/pointer bounds. @option{-g} is implied. Note
|
memory allocations and array/pointer bounds. @option{-g} is implied. Note
|
||||||
that the generated code is slower and bigger in this case.
|
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
|
@item -bt N
|
||||||
Display N callers in stack traces. This is useful with @option{-g} or
|
Display N callers in stack traces. This is useful with @option{-g} or
|
||||||
|
Loading…
Reference in New Issue
Block a user