diff --git a/i386-gen.c b/i386-gen.c index 7e1e0f49..51a514ae 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -95,7 +95,6 @@ ST_DATA const int reg_classes[NB_REGS] = { static unsigned long func_sub_sp_offset; static int func_ret_sub; #ifdef CONFIG_TCC_BCHECK -static addr_t func_bound_offset; static unsigned long func_bound_ind; #endif @@ -402,8 +401,10 @@ ST_FUNC void gfunc_call(int nb_args) Sym *func_sym; #ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check) + if (tcc_state->do_bounds_check) { + save_temp_local (nb_args); gbound_args(nb_args); + } #endif args_size = 0; @@ -485,6 +486,10 @@ ST_FUNC void gfunc_call(int nb_args) if (args_size && func_call != FUNC_STDCALL && func_call != FUNC_FASTCALLW) gadd_sp(args_size); vtop--; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + restore_temp_local (); +#endif } #ifdef TCC_TARGET_PE diff --git a/i386-link.c b/i386-link.c index 23653b29..e4929b43 100644 --- a/i386-link.c +++ b/i386-link.c @@ -235,79 +235,4 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t } } -#ifdef CONFIG_TCC_BCHECK -ST_FUNC void tcc_add_bcheck(TCCState *s1) -{ - addr_t *ptr; - int loc_glob; - int sym_index; - int bsym_index; - - if (0 == s1->do_bounds_check) - return; - /* XXX: add an object file to do that */ - ptr = section_ptr_add(bounds_section, sizeof(*ptr)); - *ptr = 0; - loc_glob = s1->output_type != TCC_OUTPUT_MEMORY ? STB_LOCAL : STB_GLOBAL; - bsym_index = set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(loc_glob, STT_NOTYPE), 0, - bounds_section->sh_num, "__bounds_start"); - /* pull bcheck.o from libtcc1.a */ - sym_index = set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - SHN_UNDEF, "__bound_init"); - if (s1->output_type != TCC_OUTPUT_MEMORY) { - /* add 'call __bound_init()' in .init section */ - Section *init_section = find_section(s1, ".init"); - unsigned char *pinit; -#ifdef TCC_TARGET_PE - pinit = section_ptr_add(init_section, 3); - pinit[0] = 0x55; /* push %rbp */ - pinit[1] = 0x89; /* mov %esp,%ebp */ - pinit[2] = 0xe5; -#endif - pinit = section_ptr_add(init_section, 5); - pinit[0] = 0xe8; - write32le(pinit + 1, -4); - put_elf_reloc(symtab_section, init_section, - init_section->data_offset - 4, R_386_PC32, sym_index); - /* R_386_PC32 = R_X86_64_PC32 = 2 */ - pinit = section_ptr_add(init_section, 6); - pinit[0] = 0xb8; /* mov xx,%eax */ - write32le(pinit + 1, 0); - pinit[5] = 0x50; /* push %eax */ - put_elf_reloc(symtab_section, init_section, - init_section->data_offset - 5, R_386_32, bsym_index); - sym_index = set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - SHN_UNDEF, "__bounds_add_static_var"); - pinit = section_ptr_add(init_section, 5); - pinit[0] = 0xe8; - write32le(pinit + 1, -4); - put_elf_reloc(symtab_section, init_section, - init_section->data_offset - 4, R_386_PC32, sym_index); - /* R_386_PC32 = R_X86_64_PC32 = 2 */ - pinit = section_ptr_add(init_section, 3); - pinit[0] = 0x83; /* add $0x4,%esp */ - pinit[1] = 0xc4; - pinit[2] = 0x04; -#ifdef TCC_TARGET_PE - { - int init_index = set_elf_sym(symtab_section, - 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - init_section->sh_num, "__init_start"); - Sym sym; - init_section->sh_flags |= SHF_EXECINSTR; - pinit = section_ptr_add(init_section, 2); - pinit[0] = 0xc9; /* leave */ - pinit[1] = 0xc3; /* ret */ - sym.c = init_index; - add_init_array (s1, &sym); - } -#endif - } -} -#endif - #endif /* !TARGET_DEFS_ONLY */ diff --git a/lib/bcheck.c b/lib/bcheck.c index fe1e82ec..29c20285 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -34,17 +34,20 @@ #include #endif -#define BOUND_DEBUG -#define BOUND_STATISTIC +#define BOUND_DEBUG (1) +#define BOUND_STATISTIC (1) -#ifdef BOUND_DEBUG - #define dprintf(a...) if (print_calls) fprintf(a) +#if BOUND_DEBUG + #define dprintf(a...) if (print_calls) fprintf(a) #else #define dprintf(a...) #endif -/* Check memalign */ -#define HAVE_MEMALIGN +#ifdef __attribute__ + /* an __attribute__ macro is defined in the system headers */ + #undef __attribute__ +#endif +#define FASTCALL __attribute__((regparm(3))) #if defined(__FreeBSD__) \ || defined(__FreeBSD_kernel__) \ @@ -52,43 +55,76 @@ || defined(__OpenBSD__) \ || defined(__NetBSD__) \ || defined(__dietlibc__) -#undef HAVE_MEMALIGN + #define INIT_SEM() #define EXIT_SEM() #define WAIT_SEM() #define POST_SEM() -#define HAS_ENVIRON 0 -#define MALLOC_REDIR (0) +#define HAVE_MEMALIGN (0) +#define HAS_ENVIRON (0) +#define MALLOC_REDIR (0) +#define HAVE_PTHREAD_CREATE (0) +#define HAVE_CTYPE (0) +#define HAVE_ERRNO (0) + #elif defined(_WIN32) + #include -#undef HAVE_MEMALIGN static CRITICAL_SECTION bounds_sem; -#define INIT_SEM() InitializeCriticalSection(&bounds_sem) -#define EXIT_SEM() DeleteCriticalSection(&bounds_sem) -#define WAIT_SEM() EnterCriticalSection(&bounds_sem) -#define POST_SEM() LeaveCriticalSection(&bounds_sem) -#define HAS_ENVIRON 0 -#define MALLOC_REDIR (0) +#define INIT_SEM() InitializeCriticalSection(&bounds_sem) +#define EXIT_SEM() DeleteCriticalSection(&bounds_sem) +#define WAIT_SEM() EnterCriticalSection(&bounds_sem) +#define POST_SEM() LeaveCriticalSection(&bounds_sem) +#define HAVE_MEMALIGN (0) +#define HAS_ENVIRON (0) +#define MALLOC_REDIR (0) +#define HAVE_PTHREAD_CREATE (0) +#define HAVE_CTYPE (0) +#define HAVE_ERRNO (0) + #else + +#define __USE_GNU /* get RTLD_NEXT */ #include +#include +#include +#include #include +#if 0 #include static sem_t bounds_sem; -#define INIT_SEM() sem_init (&bounds_sem, 0, 1) -#define EXIT_SEM() sem_destroy (&bounds_sem) -#define WAIT_SEM() while (sem_wait (&bounds_sem) < 0 && errno == EINTR); -#define POST_SEM() sem_post (&bounds_sem) -#define HAS_ENVIRON 0 /* Disabled for now */ -#define __USE_GNU /* get RTLD_NEXT */ -#include -#define MALLOC_REDIR (1) -static void *(*malloc_redir) (size_t) = NULL; -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]; +#define INIT_SEM() sem_init (&bounds_sem, 0, 1) +#define EXIT_SEM() sem_destroy (&bounds_sem) +#define WAIT_SEM() if (use_sem) while (sem_wait (&bounds_sem) < 0 \ + && errno == EINTR) +#define POST_SEM() if (use_sem) sem_post (&bounds_sem) +#else +static pthread_spinlock_t bounds_spin; +/* about 25% faster then semaphore. */ +#define INIT_SEM() pthread_spin_init (&bounds_spin, 0) +#define EXIT_SEM() pthread_spin_destroy (&bounds_spin) +#define WAIT_SEM() if (use_sem) pthread_spin_lock (&bounds_spin) +#define POST_SEM() if (use_sem) pthread_spin_unlock (&bounds_spin) +#endif +#define HAVE_MEMALIGN (1) +#define HAS_ENVIRON (1) +#define MALLOC_REDIR (1) +#define HAVE_PTHREAD_CREATE (1) +#define HAVE_CTYPE (1) +#define HAVE_ERRNO (1) + +static void *(*malloc_redir) (size_t); +static void *(*calloc_redir) (size_t, size_t); +static void (*free_redir) (void *); +static void *(*realloc_redir) (void *, size_t); +static void *(*memalign_redir) (size_t, size_t); +static int (*pthread_create_redir) (pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); +static unsigned int pool_index; +static unsigned char __attribute__((aligned(16))) initial_pool[256]; +static unsigned char use_sem; + #endif #define TCC_TYPE_NONE (0) @@ -106,8 +142,8 @@ 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 */ + unsigned char type; + unsigned char is_invalid; /* true if pointers outside region are invalid */ }; typedef struct alloca_list_struct { @@ -116,6 +152,7 @@ typedef struct alloca_list_struct { struct alloca_list_struct *next; } alloca_list_type; +#define BOUND_STATISTIC_SPLAY (0) static Tree * splay (size_t addr, Tree *t); static Tree * splay_end (size_t addr, Tree *t); static Tree * splay_insert(size_t addr, size_t size, Tree * t); @@ -123,14 +160,38 @@ static Tree * splay_delete(size_t addr, Tree *t); void splay_printtree(Tree * t, int d); /* external interface */ +void __bound_checking (int no_check); +void __bound_never_fatal (int no_check); +void * __bound_ptr_add(void *p, size_t offset); +void * __bound_ptr_indir1(void *p, size_t offset); +void * __bound_ptr_indir2(void *p, size_t offset); +void * __bound_ptr_indir4(void *p, size_t offset); +void * __bound_ptr_indir8(void *p, size_t offset); +void * __bound_ptr_indir12(void *p, size_t offset); +void * __bound_ptr_indir16(void *p, size_t offset); +void FASTCALL __bound_local_new(void *p1); +void FASTCALL __bound_local_delete(void *p1); void __bound_init(void); +void __bound_main_arg(char **p); void __bound_exit(void); - -#ifdef __attribute__ - /* an __attribute__ macro is defined in the system headers */ - #undef __attribute__ +#if !defined(_WIN32) +void *__bound_mmap (void *start, size_t size, int prot, int flags, int fd, + off_t offset); +int __bound_munmap (void *start, size_t size); #endif -#define FASTCALL __attribute__((regparm(3))) +void __bound_new_region(void *p, size_t size); +void *__bound_memcpy(void *dst, const void *src, size_t size); +int __bound_memcmp(const void *s1, const void *s2, size_t size); +void *__bound_memmove(void *dst, const void *src, size_t size); +void *__bound_memset(void *dst, int c, size_t size); +int __bound_strlen(const char *s); +char *__bound_strcpy(char *dst, const char *src); +char *__bound_strncpy(char *dst, const char *src, size_t n); +int __bound_strcmp(const char *s1, const char *s2); +int __bound_strncmp(const char *s1, const char *s2, size_t n); +char *__bound_strcat(char *dest, const char *src); +char *__bound_strchr(const char *string, int ch); +char *__bound_strdup(const char *s); #if !MALLOC_REDIR void *__bound_malloc(size_t size, const void *caller); @@ -141,30 +202,30 @@ void *__bound_calloc(size_t nmemb, size_t size); #endif #define FREE_REUSE_SIZE (100) -static int free_reuse_index = 0; +static unsigned int free_reuse_index; static void *free_reuse_list[FREE_REUSE_SIZE]; /* error message, just for TCC */ const char *__bound_error_msg; -/* runtime error output */ -extern void rt_error(size_t pc, const char *fmt, ...); - static Tree *tree = NULL; #define TREE_REUSE (1) #if TREE_REUSE -static Tree *tree_free_list = NULL; +static Tree *tree_free_list; #endif -static alloca_list_type *alloca_list = NULL; +static alloca_list_type *alloca_list; -static int inited = 0; -static int print_calls = 0; -static int print_heap = 0; -static int print_statistic = 0; -static int never_fatal = 0; -static int no_checking = 0; +static unsigned char inited; +static unsigned char print_warn_ptr_add; +static unsigned char print_calls; +static unsigned char print_heap; +static unsigned char print_statistic; +static unsigned char no_strdup; +static signed char never_fatal; +static signed char no_checking = 1; +static char exec[100]; -#ifdef BOUND_STATISTIC +#if BOUND_STATISTIC static unsigned long long bound_ptr_add_count; static unsigned long long bound_ptr_indir1_count; static unsigned long long bound_ptr_indir2_count; @@ -194,46 +255,93 @@ static unsigned long long bound_strncmp_count; static unsigned long long bound_strcat_count; static unsigned long long bound_strchr_count; static unsigned long long bound_strdup_count; -#define INCR_COUNT(x) x++ +static unsigned long long bound_not_found; +#define INCR_COUNT(x) ++x #else #define INCR_COUNT(x) #endif +#if BOUND_STATISTIC_SPLAY +static unsigned long long bound_splay; +static unsigned long long bound_splay_end; +static unsigned long long bound_splay_insert; +static unsigned long long bound_splay_delete; +#define INCR_COUNT_SPLAY(x) ++x +#else +#define INCR_COUNT_SPLAY(x) +#endif -/* enable/disable checking. This can be used for signal handlers. */ -void __bound_checking (int no_check) +/* currently only i386/x86_64 supported. Change for other platforms */ +static void fetch_and_add(signed char* variable, signed char value) { - no_checking = no_check; +#if defined __i386__ || defined __x86_64__ + __asm__ volatile("lock; addb %0, %1" + : "+r" (value), "+m" (*variable) // input+output + : // No input-only + : "memory" + ); +#else + *variable += value; +#endif } -#define no_FASTCALL -//#define no_checking 1 +/* enable/disable checking. This can be used in signal handlers. */ +void __bound_checking (int no_check) +{ + fetch_and_add (&no_checking, no_check); +} + +/* enable/disable checking. This can be used in signal handlers. */ +void __bound_never_fatal (int neverfatal) +{ + fetch_and_add (&never_fatal, neverfatal); +} /* print a bound error message */ -static void bound_error(const char *fmt, ...) +static void bound_error(const char *error) { - __bound_error_msg = fmt; - fprintf(stderr,"%s %s: %s\n", __FILE__, __FUNCTION__, fmt); + __bound_error_msg = error; + fprintf(stderr,"%s%s %s: %s\n", exec, __FILE__, __FUNCTION__, error); if (never_fatal == 0) *(void **)0 = 0; /* force a runtime error */ } -static void bound_alloc_error(void) +static void bound_alloc_error(const char *s) { - bound_error("not enough memory for bound checking code"); + fprintf(stderr,"FATAL: %s\n",s); + exit (1); +} + +static void bound_not_found_warning(const char *file, const char *function, + void *ptr) +{ + dprintf(stderr, "%s%s, %s(): Not found %p\n", exec, file, function, ptr); +} + +static void bound_ptr_add_warning(const char *file, const char *function, + void *ptr) +{ + fprintf(stderr,"%s%s %s(): %p is outside of the region\n", + exec, file, function, ptr); +} + +static void bound_ptr_indir_error(const char *file, const char *function, + void *ptr) +{ + fprintf(stderr,"%s%s %s(): %p is outside of the region\n", + exec, file, function, ptr); } /* return '(p + offset)' for pointer arithmetic (a pointer can reach the end of a region in this case */ -void * no_FASTCALL __bound_ptr_add(void *p, size_t offset) +void * __bound_ptr_add(void *p, size_t offset) { size_t addr = (size_t)p; - if (no_checking) { + if (no_checking) return p + offset; - } - dprintf(stderr, "%s %s : %p 0x%x\n", - __FILE__, __FUNCTION__, p, (unsigned)offset); + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, p, (unsigned long)offset); WAIT_SEM (); INCR_COUNT(bound_ptr_add_count); @@ -250,18 +358,21 @@ void * no_FASTCALL __bound_ptr_add(void *p, size_t offset) addr -= tree->start; } if (addr <= tree->size) { - addr += offset; - if (tree->is_invalid || addr > tree->size) { - #if 0 - fprintf(stderr,"%s %s : %p is outside of the region\n", - __FILE__, __FUNCTION__, p + offset); - #endif - if (never_fatal == 0) { - POST_SEM (); + if (tree->is_invalid || addr + offset > tree->size) { + POST_SEM (); + if (print_warn_ptr_add) + bound_ptr_add_warning (__FILE__, __FUNCTION__, p + offset); + if (never_fatal <= 0) return INVALID_POINTER; /* return an invalid pointer */ - } + return p + offset; } } + else if (p) { /* Allow NULL + offset. offsetoff is using it. */ + INCR_COUNT(bound_not_found); + POST_SEM (); + bound_not_found_warning (__FILE__, __FUNCTION__, p); + return p + offset; + } } POST_SEM (); return p + offset; @@ -270,15 +381,15 @@ void * no_FASTCALL __bound_ptr_add(void *p, size_t offset) /* return '(p + offset)' for pointer indirection (the resulting must be strictly inside the region */ #define BOUND_PTR_INDIR(dsize) \ -void * no_FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \ +void * __bound_ptr_indir ## dsize (void *p, size_t offset) \ { \ size_t addr = (size_t)p; \ \ - if (no_checking) { \ + if (no_checking) \ return p + offset; \ - } \ - dprintf(stderr, "%s %s : %p 0x%x start\n", \ - __FILE__, __FUNCTION__, p, (unsigned)offset); \ + \ + dprintf(stderr, "%s, %s(): %p 0x%lx\n", \ + __FILE__, __FUNCTION__, p, (unsigned long)offset); \ WAIT_SEM (); \ INCR_COUNT(bound_ptr_indir ## dsize ## _count); \ if (tree) { \ @@ -294,16 +405,21 @@ void * no_FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \ addr -= tree->start; \ } \ if (addr <= tree->size) { \ - addr += offset + dsize; \ - if (tree->is_invalid || addr > tree->size) { \ - fprintf(stderr,"%s %s : %p is outside of the region\n", \ - __FILE__, __FUNCTION__, p + offset); \ - if (never_fatal == 0) { \ - POST_SEM (); \ + if (tree->is_invalid || addr + offset + dsize > tree->size) { \ + POST_SEM (); \ + bound_ptr_indir_error (__FILE__, __FUNCTION__, \ + p + offset); \ + if (never_fatal <= 0) \ return INVALID_POINTER; /* return an invalid pointer */ \ - } \ + return p + offset; \ } \ } \ + else { \ + INCR_COUNT(bound_not_found); \ + POST_SEM (); \ + bound_not_found_warning (__FILE__, __FUNCTION__, p); \ + return p + offset; \ + } \ } \ POST_SEM (); \ return p + offset; \ @@ -334,90 +450,180 @@ BOUND_PTR_INDIR(16) /* called when entering a function to add all the local regions */ void FASTCALL __bound_local_new(void *p1) { - size_t addr, size, fp, *p = p1; + size_t addr, fp, *p = p1; if (no_checking) return; GET_CALLER_FP(fp); - dprintf(stderr, "%s, %s local new p1=%p fp=%p\n", - __FILE__, __FUNCTION__, p, (void *)fp); + dprintf(stderr, "%s, %s(): p1=%p fp=%p\n", + __FILE__, __FUNCTION__, p, (void *)fp); WAIT_SEM (); - for(;;) { - addr = p[0]; - if (addr == 0) - break; + while ((addr = p[0])) { INCR_COUNT(bound_local_new_count); - if (addr == 1) { - dprintf(stderr, "%s, %s() alloca/vla used\n", - __FILE__, __FUNCTION__); - } - else { - addr += fp; - size = p[1]; - dprintf(stderr, "%s, %s() (%p 0x%lx)\n", - __FILE__, __FUNCTION__, (void *) addr, (unsigned long) size); - tree = splay_insert(addr, size, tree); - } + if (addr != 1) + tree = splay_insert(addr + fp, p[1], tree); p += 2; } POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + p = p1; + while ((addr = p[0])) { + if (addr == 1) { + dprintf(stderr, "%s, %s(): alloca/vla used\n", + __FILE__, __FUNCTION__); + } + else { + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, + (void *) (addr + fp), (unsigned long) p[1]); + } + p += 2; + } + } +#endif } /* called when leaving a function to delete all the local regions */ void FASTCALL __bound_local_delete(void *p1) { size_t addr, fp, *p = p1; + alloca_list_type *free_list = NULL; if (no_checking) return; GET_CALLER_FP(fp); - dprintf(stderr, "%s, %s local delete p1=%p fp=%p\n", + dprintf(stderr, "%s, %s(): p1=%p fp=%p\n", __FILE__, __FUNCTION__, p, (void *)fp); WAIT_SEM (); - for(;;) { - addr = p[0]; - if (addr == 0) - break; + while ((addr = p[0])) { INCR_COUNT(bound_local_delete_count); if (addr == 1) { - while (alloca_list && alloca_list->fp == fp) { - alloca_list_type *next = alloca_list->next; + alloca_list_type *last = NULL; + alloca_list_type *cur = alloca_list; - dprintf(stderr, "%s, %s() remove alloca/vla %p\n", - __FILE__, __FUNCTION__, alloca_list->p); - tree = splay_delete ((size_t) alloca_list->p, tree); -#if MALLOC_REDIR - free_redir (alloca_list); -#else - free (alloca_list); -#endif - alloca_list = next; + while (cur) { + if (cur->fp == fp) { + if (last) + last->next = cur->next; + else + alloca_list = cur->next; + tree = splay_delete ((size_t) cur->p, tree); + cur->next = free_list; + free_list = cur; + cur = last ? last->next : alloca_list; + } + else { + last = cur; + cur = cur->next; + } } } - else { - addr += fp; - dprintf(stderr, "%s, %s() (%p 0x%lx)\n", - __FILE__, __FUNCTION__, (void *) addr, (unsigned long) p[1]); - tree = splay_delete(addr, tree); - } + else + tree = splay_delete(addr + fp, tree); p += 2; } POST_SEM (); + while (free_list) { + alloca_list_type *next = free_list->next; + + dprintf(stderr, "%s, %s(): remove alloca/vla %p\n", + __FILE__, __FUNCTION__, free_list->p); +#if MALLOC_REDIR + free_redir (free_list); +#else + free (free_list); +#endif + free_list = next; + } +#if BOUND_DEBUG + if (print_calls) { + p = p1; + while ((addr = p[0])) { + if (addr != 1) { + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, + (void *) (addr + fp), (unsigned long) p[1]); + } + p+= 2; + } + } +#endif } -void __bound_init(void) +/* used by alloca */ +void __bound_new_region(void *p, size_t size) { + size_t fp; + alloca_list_type *last; + alloca_list_type *cur; + alloca_list_type *new; + + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, p, (unsigned long)size); + GET_CALLER_FP (fp); +#if MALLOC_REDIR + new = malloc_redir (sizeof (alloca_list_type)); +#else + new = malloc (sizeof (alloca_list_type)); +#endif + WAIT_SEM (); + INCR_COUNT(bound_alloca_count); + last = NULL; + cur = alloca_list; + while (cur) { + if (cur->fp == fp && cur->p == p) { + if (last) + last->next = cur->next; + else + alloca_list = cur->next; + tree = splay_delete((size_t)p, tree); + break; + } + last = cur; + cur = cur->next; + } + if (no_checking == 0) + tree = splay_insert((size_t)p, size, tree); + if (new) { + new->fp = fp; + new->p = p; + new->next = alloca_list; + alloca_list = new; + } + POST_SEM (); + if (cur) { + dprintf(stderr, "%s, %s(): remove alloca/vla %p\n", + __FILE__, __FUNCTION__, cur->p); +#if MALLOC_REDIR + free_redir (cur); +#else + free (cur); +#endif + } +} + +#if defined(__GNUC__) && (__GNUC__ >= 6) +#pragma GCC diagnostic pop +#endif + +void __attribute__((constructor)) __bound_init(void) +{ + extern size_t __bounds_start[]; + size_t *p; + if (inited) return; inited = 1; + print_warn_ptr_add = getenv ("TCC_BOUNDS_WARN_POINTER_ADD") != NULL; print_calls = getenv ("TCC_BOUNDS_PRINT_CALLS") != NULL; print_heap = getenv ("TCC_BOUNDS_PRINT_HEAP") != NULL; print_statistic = getenv ("TCC_BOUNDS_PRINT_STATISTIC") != 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__); INIT_SEM (); @@ -428,7 +634,7 @@ void __bound_init(void) /* tcc -run required RTLD_DEFAULT. Normal usage requires RTLD_NEXT */ *(void **) (&malloc_redir) = dlsym (addr, "malloc"); if (malloc_redir == NULL) { - dprintf(stderr, "%s, %s() use RTLD_DEFAULT\n", + dprintf(stderr, "%s, %s(): use RTLD_DEFAULT\n", __FILE__, __FUNCTION__); addr = RTLD_DEFAULT; *(void **) (&malloc_redir) = dlsym (addr, "malloc"); @@ -437,37 +643,109 @@ void __bound_init(void) *(void **) (&free_redir) = dlsym (addr, "free"); *(void **) (&realloc_redir) = dlsym (addr, "realloc"); *(void **) (&memalign_redir) = dlsym (addr, "memalign"); - dprintf(stderr, "%s, %s() malloc_redir %p\n", + dprintf(stderr, "%s, %s(): malloc_redir %p\n", __FILE__, __FUNCTION__, malloc_redir); - dprintf(stderr, "%s, %s() free_redir %p\n", + dprintf(stderr, "%s, %s(): free_redir %p\n", __FILE__, __FUNCTION__, free_redir); - dprintf(stderr, "%s, %s() realloc_redir %p\n", + dprintf(stderr, "%s, %s(): realloc_redir %p\n", __FILE__, __FUNCTION__, realloc_redir); - dprintf(stderr, "%s, %s() memalign_redir %p\n", + dprintf(stderr, "%s, %s(): memalign_redir %p\n", __FILE__, __FUNCTION__, memalign_redir); + if (malloc_redir == NULL || free_redir == NULL) + bound_alloc_error ("Cannot redirect malloc/free"); +#if HAVE_PTHREAD_CREATE + *(void **) (&pthread_create_redir) = dlsym (addr, "pthread_create"); + dprintf(stderr, "%s, %s(): pthread_create_redir %p\n", + __FILE__, __FUNCTION__, pthread_create_redir); +#endif } #endif - tree = NULL; +#ifdef __linux__ + { + FILE *fp; + unsigned char found; + unsigned long long start; + unsigned long long end; + unsigned long long ad = + (unsigned long long) __builtin_return_address(0); + char line[1000]; - /* save malloc hooks and install bound check hooks */ - memset (free_reuse_list, 0, sizeof (free_reuse_list)); + /* Display exec name. Usefull when a lot of code is compiled with tcc */ + fp = fopen ("/proc/self/comm", "r"); + if (fp) { + memset (exec, 0, sizeof(exec)); + fread (exec, 1, sizeof(exec) - 2, fp); + if (strchr(exec,'\n')) + *strchr(exec,'\n') = '\0'; + strcat (exec, ":"); + fclose (fp); + } + /* check if dlopen is used (is threre a better way?) */ + found = 0; + fp = fopen ("/proc/self/maps", "r"); + if (fp) { + while (fgets (line, sizeof(line), fp)) { + if (sscanf (line, "%Lx-%Lx", &start, &end) == 2 && + ad >= start && ad < end) { + found = 1; + break; + } + if (strstr (line,"[heap]")) + break; + } + fclose (fp); + } + if (found == 0) { + use_sem = 1; + no_strdup = 1; + } + } +#endif - dprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__); -} - -void __bounds_add_static_var (size_t *p) -{ - dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__); - /* add all static bound check values */ WAIT_SEM (); + +#if HAVE_CTYPE + /* XXX: Does not work if locale is changed */ + tree = splay_insert((size_t) __ctype_b_loc(), + sizeof (unsigned short *), tree); + tree = splay_insert((size_t) (*__ctype_b_loc() - 128), + 384 * sizeof (unsigned short), tree); + tree = splay_insert((size_t) __ctype_tolower_loc(), + sizeof (__int32_t *), tree); + tree = splay_insert((size_t) (*__ctype_tolower_loc() - 128), + 384 * sizeof (__int32_t), tree); + tree = splay_insert((size_t) __ctype_toupper_loc(), + sizeof (__int32_t *), tree); + tree = splay_insert((size_t) (*__ctype_toupper_loc() - 128), + 384 * sizeof (__int32_t), tree); +#endif +#if HAVE_ERRNO + tree = splay_insert((size_t) (&errno), sizeof (int), tree); +#endif + + /* add all static bound check values */ + p = __bounds_start; while (p[0] != 0) { - dprintf(stderr, "%s, %s() (%p 0x%lx)\n", - __FILE__, __FUNCTION__, (void *) p[0], (unsigned long) p[1]); tree = splay_insert(p[0], p[1], tree); p += 2; } POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + p = __bounds_start; + while (p[0] != 0) { + dprintf(stderr, "%s, %s(): static var %p 0x%lx\n", + __FILE__, __FUNCTION__, + (void *) p[0], (unsigned long) p[1]); + p += 2; + } + } +#endif + + no_checking = 0; + + dprintf(stderr, "%s, %s(): end\n\n", __FILE__, __FUNCTION__); } void __bound_main_arg(char **p) @@ -476,33 +754,55 @@ void __bound_main_arg(char **p) WAIT_SEM (); while (*p) { - dprintf(stderr, "%s, %s() (%p 0x%lx)\n", - __FILE__, __FUNCTION__, *p, (unsigned long)(strlen (*p) + 1)); tree = splay_insert((size_t) *p, strlen (*p) + 1, tree); - p++; + ++p; } - dprintf(stderr, "%s, %s() argv (%p 0x%lx)\n", - __FILE__, __FUNCTION__, start, (unsigned long)((char *) p - start)); tree = splay_insert((size_t) start, (char *) p - start, tree); + POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + p = (char **) start; + while (*p) { + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, + *p, (unsigned long)(strlen (*p) + 1)); + ++p; + } + dprintf(stderr, "%s, %s(): argv %p 0x%lx\n", + __FILE__, __FUNCTION__, + start, (unsigned long)((char *) p - start)); + } +#endif #if HAS_ENVIRON { extern char **environ; + WAIT_SEM (); p = environ; start = (char *) p; while (*p) { - dprintf(stderr, "%s, %s() (%p 0x%lx)\n", - __FILE__, __FUNCTION__, *p, (unsigned long)(strlen (*p) + 1)); tree = splay_insert((size_t) *p, strlen (*p) + 1, tree); - p++; + ++p; } - dprintf(stderr, "%s, %s() environ(%p 0x%lx)\n", - __FILE__, __FUNCTION__, start, (unsigned long)((char *) p - start)); tree = splay_insert((size_t) start, (char *) p - start, tree); + POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + p = environ; + while (*p) { + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, + *p, (unsigned long)(strlen (*p) + 1)); + ++p; + } + dprintf(stderr, "%s, %s(): environ %p 0x%lx\n", + __FILE__, __FUNCTION__, + start, (unsigned long)((char *) p - start)); + } +#endif } #endif - POST_SEM (); } void __attribute__((destructor)) __bound_exit(void) @@ -512,16 +812,30 @@ void __attribute__((destructor)) __bound_exit(void) "", "malloc", "calloc", "realloc", "memalign", "strdup" }; - dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__); + dprintf(stderr, "%s, %s():\n", __FILE__, __FUNCTION__); if (inited) { #if !defined(_WIN32) if (print_heap) { - extern void __libc_freeres (); + extern void __libc_freeres (void); __libc_freeres (); } #endif - + + no_checking = 1; + + WAIT_SEM (); + while (alloca_list) { + alloca_list_type *next = alloca_list->next; + + tree = splay_delete ((size_t) alloca_list->p, tree); +#if MALLOC_REDIR + free_redir (alloca_list); +#else + free (alloca_list); +#endif + alloca_list = next; + } for (i = 0; i < FREE_REUSE_SIZE; i++) { if (free_reuse_list[i]) { tree = splay_delete ((size_t) free_reuse_list[i], tree); @@ -533,11 +847,10 @@ void __attribute__((destructor)) __bound_exit(void) } } while (tree) { - if (print_heap && tree->type != 0) { - fprintf (stderr, "%s, %s() %s found size %lu\n", + if (print_heap && tree->type != 0) + fprintf (stderr, "%s, %s(): %s found size %lu\n", __FILE__, __FUNCTION__, alloc_type[tree->type], (unsigned long) tree->size); - } tree = splay_delete (tree->start, tree); } #if TREE_REUSE @@ -551,10 +864,11 @@ void __attribute__((destructor)) __bound_exit(void) tree_free_list = next; } #endif + POST_SEM (); EXIT_SEM (); inited = 0; -#ifdef BOUND_STATISTIC if (print_statistic) { +#if BOUND_STATISTIC fprintf (stderr, "bound_ptr_add_count %llu\n", bound_ptr_add_count); fprintf (stderr, "bound_ptr_indir1_count %llu\n", bound_ptr_indir1_count); fprintf (stderr, "bound_ptr_indir2_count %llu\n", bound_ptr_indir2_count); @@ -584,14 +898,28 @@ void __attribute__((destructor)) __bound_exit(void) fprintf (stderr, "bound_strcat_count %llu\n", bound_strcat_count); fprintf (stderr, "bound_strchr_count %llu\n", bound_strchr_count); fprintf (stderr, "bound_strdup_count %llu\n", bound_strdup_count); - } + fprintf (stderr, "bound_not_found %llu\n", bound_not_found); #endif +#if BOUND_STATISTIC_SPLAY + fprintf (stderr, "bound_splay %llu\n", bound_splay); + fprintf (stderr, "bound_splay_end %llu\n", bound_splay_end); + fprintf (stderr, "bound_splay_insert %llu\n", bound_splay_insert); + fprintf (stderr, "bound_splay_delete %llu\n", bound_splay_delete); +#endif + } } } -/* XXX: we should use a malloc which ensure that it is unlikely that - two malloc'ed data have the same address if 'free' are made in - between. */ +#if HAVE_PTHREAD_CREATE +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) +{ + use_sem = 1; + dprintf (stderr, "%s, %s()n", __FILE__, __FUNCTION__); + return pthread_create_redir(thread, attr, start_routine, arg); +} +#endif + #if MALLOC_REDIR void *malloc(size_t size) #else @@ -603,34 +931,40 @@ 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) { - 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); - return ptr; + __bound_init (); + if (malloc_redir == NULL) { + ptr = &initial_pool[pool_index]; + pool_index = (pool_index + size + 15) & ~15; + if (pool_index >= sizeof (initial_pool)) + bound_alloc_error ("initial memory pool too small"); + dprintf (stderr, "%s, %s(): initial %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + return ptr; + } } #endif /* we allocate one more byte to ensure the regions will be separated by at least one byte. With the glibc malloc, it may be in fact not necessary */ - WAIT_SEM (); - INCR_COUNT(bound_malloc_count); #if MALLOC_REDIR - ptr = malloc_redir (size); + ptr = malloc_redir (size + 1); #else ptr = malloc(size + 1); #endif + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); - dprintf(stderr, "%s, %s (%p, 0x%x)\n", - __FILE__, __FUNCTION__, ptr, (unsigned)size); + if (no_checking == 0) { + WAIT_SEM (); + INCR_COUNT(bound_malloc_count); - if (ptr) { - tree = splay_insert ((size_t) ptr, size, tree); - if (tree && tree->start == (size_t) ptr) { - tree->type = TCC_TYPE_MALLOC; + if (ptr) { + tree = splay_insert ((size_t) ptr, size, tree); + if (tree && tree->start == (size_t) ptr) + tree->type = TCC_TYPE_MALLOC; } + POST_SEM (); } - POST_SEM (); return ptr; } @@ -642,10 +976,16 @@ void *__bound_memalign(size_t size, size_t align, const void *caller) { void *ptr; - WAIT_SEM (); - INCR_COUNT(bound_memalign_count); - -#ifndef HAVE_MEMALIGN +#if HAVE_MEMALIGN + /* we allocate one more byte to ensure the regions will be + separated by at least one byte. With the glibc malloc, it may + be in fact not necessary */ +#if MALLOC_REDIR + ptr = memalign_redir(size + 1, align); +#else + ptr = memalign(size + 1, align); +#endif +#else if (align > 4) { /* XXX: handle it ? */ ptr = NULL; @@ -657,25 +997,21 @@ void *__bound_memalign(size_t size, size_t align, const void *caller) ptr = malloc(size + 1); #endif } -#else - /* we allocate one more byte to ensure the regions will be - separated by at least one byte. With the glibc malloc, it may - be in fact not necessary */ -#if MALLOC_REDIR - ptr = memalign_redir(size + 1, align); -#else - ptr = memalign(size + 1, align); #endif -#endif - if (ptr) { - dprintf(stderr, "%s, %s (%p, 0x%x)\n", - __FILE__, __FUNCTION__, ptr, (unsigned)size); - tree = splay_insert((size_t) ptr, size, tree); - if (tree && tree->start == (size_t) ptr) { - tree->type = TCC_TYPE_MEMALIGN; + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + + if (no_checking == 0) { + WAIT_SEM (); + INCR_COUNT(bound_memalign_count); + + if (ptr) { + tree = splay_insert((size_t) ptr, size, tree); + if (tree && tree->start == (size_t) ptr) + tree->type = TCC_TYPE_MEMALIGN; } + POST_SEM (); } - POST_SEM (); return ptr; } @@ -688,7 +1024,7 @@ 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 || no_checking #if MALLOC_REDIR || ((unsigned char *) ptr >= &initial_pool[0] && (unsigned char *) ptr < &initial_pool[sizeof(initial_pool)]) @@ -696,15 +1032,15 @@ void __bound_free(void *ptr, const void *caller) ) return; - dprintf(stderr, "%s, %s (%p)\n", __FILE__, __FUNCTION__, ptr); + dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr); WAIT_SEM (); INCR_COUNT(bound_free_count); tree = splay (addr, tree); if (tree->start == addr) { if (tree->is_invalid) { - bound_error("freeing invalid region"); POST_SEM (); + bound_error("freeing invalid region"); return; } tree->is_invalid = 1; @@ -712,17 +1048,16 @@ void __bound_free(void *ptr, const void *caller) p = free_reuse_list[free_reuse_index]; free_reuse_list[free_reuse_index] = ptr; free_reuse_index = (free_reuse_index + 1) % FREE_REUSE_SIZE; - if (p) { + if (p) tree = splay_delete((size_t)p, tree); - } ptr = p; } + POST_SEM (); #if MALLOC_REDIR free_redir (ptr); #else free(ptr); #endif - POST_SEM (); } #if MALLOC_REDIR @@ -731,6 +1066,8 @@ void *realloc(void *ptr, size_t size) void *__bound_realloc(void *ptr, size_t size, const void *caller) #endif { + void *new_ptr; + if (size == 0) { #if MALLOC_REDIR free(ptr); @@ -739,40 +1076,29 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller) #endif return NULL; } - else if (ptr == NULL) { + #if MALLOC_REDIR - ptr = realloc_redir (ptr, size); + new_ptr = realloc_redir (ptr, size); #else - ptr = realloc (ptr, size); + new_ptr = realloc (ptr, size); #endif + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, new_ptr, (unsigned long)size); + + if (no_checking == 0) { WAIT_SEM (); INCR_COUNT(bound_realloc_count); - if (ptr) { - tree = splay_insert ((size_t) ptr, size, tree); - if (tree && tree->start == (size_t) ptr) { + + if (ptr) + tree = splay_delete ((size_t) ptr, tree); + if (new_ptr) { + tree = splay_insert ((size_t) new_ptr, size, tree); + if (tree && tree->start == (size_t) new_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 - ptr = realloc (ptr, size); -#endif - if (ptr) { - tree = splay_insert ((size_t) ptr, size, tree); - if (tree && tree->start == (size_t) ptr) { - tree->type = TCC_TYPE_REALLOC; - } - } - POST_SEM (); - return ptr; } + return new_ptr; } #if MALLOC_REDIR @@ -787,12 +1113,17 @@ void *__bound_calloc(size_t nmemb, size_t size) #if MALLOC_REDIR /* 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; + __bound_init (); + if (malloc_redir == NULL) { + ptr = &initial_pool[pool_index]; + pool_index = (pool_index + size + 15) & ~15; + if (pool_index >= sizeof (initial_pool)) + bound_alloc_error ("initial memory pool too small"); + dprintf (stderr, "%s, %s(): initial %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + memset (ptr, 0, size); + return ptr; + } } #endif #if MALLOC_REDIR @@ -800,15 +1131,19 @@ void *__bound_calloc(size_t nmemb, size_t size) #else ptr = malloc(size + 1); #endif + dprintf (stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + if (ptr) { memset (ptr, 0, size); - WAIT_SEM (); - INCR_COUNT(bound_calloc_count); - tree = splay_insert ((size_t) ptr, size, tree); - if (tree && tree->start == (size_t) ptr) { - tree->type = TCC_TYPE_CALLOC; + if (no_checking == 0) { + WAIT_SEM (); + INCR_COUNT(bound_calloc_count); + tree = splay_insert ((size_t) ptr, size, tree); + if (tree && tree->start == (size_t) ptr) + tree->type = TCC_TYPE_CALLOC; + POST_SEM (); } - POST_SEM (); } return ptr; } @@ -819,10 +1154,10 @@ void *__bound_mmap (void *start, size_t size, int prot, { void *result; - dprintf(stderr, "%s, %s (%p, 0x%x)\n", - __FILE__, __FUNCTION__, start, (unsigned)size); + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, start, (unsigned long)size); result = mmap (start, size, prot, flags, fd, offset); - if (result) { + if (result && no_checking == 0) { WAIT_SEM (); INCR_COUNT(bound_mmap_count); tree = splay_insert((size_t)result, size, tree); @@ -835,161 +1170,151 @@ int __bound_munmap (void *start, size_t size) { int result; - dprintf(stderr, "%s, %s (%p, 0x%x)\n", - __FILE__, __FUNCTION__, start, (unsigned)size); - WAIT_SEM (); - INCR_COUNT(bound_munmap_count); - tree = splay_delete ((size_t) start, tree); - POST_SEM (); + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, start, (unsigned long)size); + if (start && no_checking == 0) { + WAIT_SEM (); + INCR_COUNT(bound_munmap_count); + tree = splay_delete ((size_t) start, tree); + POST_SEM (); + } result = munmap (start, size); return result; } #endif -/* used by alloca */ -void __bound_new_region(void *p, size_t size) -{ - size_t fp; - alloca_list_type *last; - alloca_list_type *cur; - - dprintf(stderr, "%s, %s (%p, 0x%x)\n", - __FILE__, __FUNCTION__, p, (unsigned)size); - WAIT_SEM (); - INCR_COUNT(bound_alloca_count); - GET_CALLER_FP (fp); - last = NULL; - cur = alloca_list; - while (cur && cur->fp == fp) { - if (cur->p == p) { - dprintf(stderr, "%s, %s() remove alloca/vla %p\n", - __FILE__, __FUNCTION__, alloca_list->p); - if (last) { - last->next = cur->next; - } - else { - alloca_list = cur->next; - } - tree = splay_delete((size_t)p, tree); -#if MALLOC_REDIR - free_redir (cur); -#else - free (cur); -#endif - break; - } - last = cur; - cur = cur->next; - } - tree = splay_insert((size_t)p, size, tree); -#if MALLOC_REDIR - cur = malloc_redir (sizeof (alloca_list_type)); -#else - cur = malloc (sizeof (alloca_list_type)); -#endif - if (cur) { - cur->fp = fp; - cur->p = p; - cur->next = alloca_list; - alloca_list = cur; - } - 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 */ static void __bound_check(const void *p, size_t size, const char *function) { - if (no_checking) - return; - if (size == 0) - return; - p = __bound_ptr_add((void *)p, size); - if (p == INVALID_POINTER) - bound_error("invalid pointer"); + if (no_checking == 0 && size != 0 && + __bound_ptr_add((void *)p, size) == INVALID_POINTER) { + static char line[100]; + sprintf(line, "invalid pointer %p, size 0x%lx in %s", + p, (unsigned long)size, function); + bound_error(line); + } } -void *__bound_memcpy(void *dst, const void *src, size_t size) +static int check_overlap (const void *p1, size_t n1, + const void *p2, size_t n2, + const char *function) { - void* p; + const void *p1e = (const void *) ((const char *) p1 + n1); + const void *p2e = (const void *) ((const char *) p2 + n2); + if (no_checking == 0 && n1 != 0 && n2 !=0 && + ((p1 <= p2 && p1e > p2) || /* p1----p2====p1e----p2e */ + (p2 <= p1 && p2e > p1))) { /* p2----p1====p2e----p1e */ + static char line[100]; + sprintf(line, "overlapping regions %p(0x%lx), %p(0x%lx) in %s", + p1, (unsigned long)n1, p2, (unsigned long)n2, function); + bound_error(line); + return never_fatal < 0; + } + return 0; +} + +void *__bound_memcpy(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); INCR_COUNT(bound_mempcy_count); - __bound_check(dst, size, "memcpy"); - __bound_check(src, size, "memcpy"); - /* check also region overlap */ - if (no_checking == 0 && src >= dst && src < dst + size) - bound_error("overlapping regions in memcpy()"); - - p = memcpy(dst, src, size); - - return p; + __bound_check(dest, n, "memcpy dest"); + __bound_check(src, n, "memcpy src"); + if (check_overlap(dest, n, src, n, "memcpy")) + return dest; + return memcpy(dest, src, n); } -int __bound_memcmp(const void *s1, const void *s2, size_t size) +int __bound_memcmp(const void *s1, const void *s2, size_t n) { + const unsigned char *u1 = (const unsigned char *) s1; + const unsigned char *u2 = (const unsigned char *) s2; + int retval = 0; + + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, s1, s2, (unsigned long)n); INCR_COUNT(bound_memcmp_count); - __bound_check(s1, size, "memcmp"); - __bound_check(s2, size, "memcmp"); - return memcmp(s1, s2, size); + for (;;) { + if ((ssize_t) --n == -1) + break; + else if (*u1 != *u2) { + retval = *u1++ - *u2++; + break; + } + ++u1; + ++u2; + } + __bound_check(s1, (const void *)u1 - s1, "memcmp s1"); + __bound_check(s2, (const void *)u2 - s2, "memcmp s2"); + return retval; } -void *__bound_memmove(void *dst, const void *src, size_t size) +void *__bound_memmove(void *dest, const void *src, size_t n) { + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); INCR_COUNT(bound_memmove_count); - __bound_check(dst, size, "memmove"); - __bound_check(src, size, "memmove"); - return memmove(dst, src, size); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return memmove(dest, src, n); } -void *__bound_memset(void *dst, int c, size_t size) +void *__bound_memset(void *s, int c, size_t n) { + dprintf(stderr, "%s, %s(): %p, %d, 0x%lx\n", + __FILE__, __FUNCTION__, s, c, (unsigned long)n); INCR_COUNT(bound_memset_count); - __bound_check(dst, size, "memset"); - return memset(dst, c, size); + __bound_check(s, n, "memset"); + return memset(s, c, n); } int __bound_strlen(const char *s) { const char *p = s; - size_t len; + dprintf(stderr, "%s, %s(): %p\n", + __FILE__, __FUNCTION__, s); INCR_COUNT(bound_strlen_count); while (*p++); - len = (p - s) - 1; - p = __bound_ptr_indir1((char *)s, len); - if (p == INVALID_POINTER) - bound_error("bad pointer in strlen()"); - return len; + __bound_check(s, p - s, "strlen"); + return (p - s) - 1; } -char *__bound_strcpy(char *dst, const char *src) +char *__bound_strcpy(char *dest, const char *src) { size_t len; - void *p; + const char *p = src; + dprintf(stderr, "%s, %s(): %p, %p\n", + __FILE__, __FUNCTION__, dest, src); INCR_COUNT(bound_strcpy_count); - len = __bound_strlen(src); - p = __bound_memcpy(dst, src, len + 1); - return p; + while (*p++); + len = p - src; + __bound_check(dest, len, "strcpy dest"); + __bound_check(src, len, "strcpy src"); + if (check_overlap(dest, len, src, len, "strcpy")) + return dest; + return strcpy (dest, src); } -char *__bound_strncpy(char *dst, const char *src, size_t n) +char *__bound_strncpy(char *dest, const char *src, size_t n) { size_t len = n; const char *p = src; + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_strncpy_count); while (len-- && *p++); len = p - src; - INCR_COUNT(bound_strncpy_count); - __bound_check(dst, len, "strncpy"); - __bound_check(src, len, "strncpy"); - return strncpy (dst, src, n); + __bound_check(dest, len, "strncpy dest"); + __bound_check(src, len, "strncpy src"); + if (check_overlap(dest, len, src, len, "strncpy")) + return dest; + return strncpy(dest, src, n); } int __bound_strcmp(const char *s1, const char *s2) @@ -997,14 +1322,16 @@ int __bound_strcmp(const char *s1, const char *s2) const unsigned char *u1 = (const unsigned char *) s1; const unsigned char *u2 = (const unsigned char *) s2; + dprintf(stderr, "%s, %s(): %p, %p\n", + __FILE__, __FUNCTION__, s1, s2); INCR_COUNT(bound_strcmp_count); while (*u1 && *u1 == *u2) { - u1++; - u2++; + ++u1; + ++u2; } - __bound_check(s1, ((const char *)u1 - s1) + 1, "strcmp"); - __bound_check(s2, ((const char *)u2 - s2) + 1, "strcmp"); - return (*u1 - *u2); + __bound_check(s1, ((const char *)u1 - s1) + 1, "strcmp s1"); + __bound_check(s2, ((const char *)u2 - s2) + 1, "strcmp s2"); + return *u1 - *u2; } int __bound_strncmp(const char *s1, const char *s2, size_t n) @@ -1013,18 +1340,20 @@ int __bound_strncmp(const char *s1, const char *s2, size_t n) const unsigned char *u2 = (const unsigned char *) s2; int retval = 0; + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, s1, s2, (unsigned long)n); INCR_COUNT(bound_strncmp_count); do { if ((ssize_t) --n == -1) break; else if (*u1 != *u2) { - retval = *u1 - *u2; + retval = *u1++ - *u2++; break; } - u2++; + ++u2; } while (*u1++); - __bound_check(s1, ((const char *)u1 - s1) + 1, "strncmp"); - __bound_check(s2, ((const char *)u1 - s1) + 1, "strncmp"); + __bound_check(s1, (const char *)u1 - s1, "strncmp s1"); + __bound_check(s2, (const char *)u2 - s2, "strncmp s2"); return retval; } @@ -1033,29 +1362,33 @@ char *__bound_strcat(char *dest, const char *src) char *r = dest; const char *s = src; + dprintf(stderr, "%s, %s(): %p, %p\n", + __FILE__, __FUNCTION__, dest, src); INCR_COUNT(bound_strcat_count); while (*dest++); - dest--; - while ((*dest++ = *src++) != 0); - __bound_check(r, dest - r, "strcat"); - __bound_check(s, src - s, "strcat"); - return r; + while (*src++); + __bound_check(r, (dest - r) + (src - s) - 1, "strcat dest"); + __bound_check(s, src - s, "strcat src"); + if (check_overlap(r, (dest - r) + (src - s) - 1, s, src - s, "strcat")) + return dest; + return strcat(r, s); } -char *__bound_strchr(const char *string, int ch) +char *__bound_strchr(const char *s, int c) { - const unsigned char *s = (const unsigned char *) string; - unsigned char c = ch; + const unsigned char *str = (const unsigned char *) s; + unsigned char ch = c; + dprintf(stderr, "%s, %s(): %p, %d\n", + __FILE__, __FUNCTION__, s, ch); INCR_COUNT(bound_strchr_count); - while (*s) { - if (*s == c) { + while (*str) { + if (*str == ch) break; - } - s++; + ++str; } - __bound_check(string, ((const char *)s - string) + 1, "strchr"); - return *s == c ? (char *) s : NULL; + __bound_check(s, ((const char *)str - s) + 1, "strchr"); + return *str == ch ? (char *) str : NULL; } char *__bound_strdup(const char *s) @@ -1067,18 +1400,21 @@ char *__bound_strdup(const char *s) while (*p++); __bound_check(s, p - s, "strdup"); #if MALLOC_REDIR - new = malloc_redir (p - s); + new = malloc_redir ((p - s) + 1); #else - new = malloc (p - s); + new = malloc ((p - s) + 1); #endif + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, new, (unsigned long)(p -s)); if (new) { - WAIT_SEM (); - tree = splay_insert((size_t)new, p - s, tree); - if (tree && tree->start == (size_t) new) { - tree->type = TCC_TYPE_STRDUP; + if (no_checking == 0 && no_strdup == 0) { + WAIT_SEM (); + tree = splay_insert((size_t)new, p - s, tree); + if (tree && tree->start == (size_t) new) + tree->type = TCC_TYPE_STRDUP; + POST_SEM (); } memcpy (new, s, p - s); - POST_SEM (); } return new; } @@ -1136,16 +1472,14 @@ char *__bound_strdup(const char *s) #define compare(start,tstart,tsize) (start < tstart ? -1 : \ start >= tstart+tsize ? 1 : 0) -/* This is the comparison. */ -/* Returns <0 if i0 if i>j */ - static Tree * splay (size_t addr, Tree *t) /* Splay using the key start (which may or may not be in the tree.) */ -/* The starting root is t, and the tree used is defined by rat */ +/* The starting root is t, and the tree used is defined by rat */ { Tree N, *l, *r, *y; int comp; + INCR_COUNT_SPLAY(bound_splay); if (t == NULL) return t; N.left = N.right = NULL; l = r = &N; @@ -1198,6 +1532,7 @@ static Tree * splay_end (size_t addr, Tree *t) Tree N, *l, *r, *y; int comp; + INCR_COUNT_SPLAY(bound_splay_end); if (t == NULL) return t; N.left = N.right = NULL; l = r = &N; @@ -1242,10 +1577,11 @@ static Tree * splay_end (size_t addr, Tree *t) static Tree * splay_insert(size_t addr, size_t size, Tree * t) /* Insert key start into the tree t, if it is not already there. */ -/* Return a pointer to the resulting tree. */ +/* Return a pointer to the resulting tree. */ { Tree * new; + INCR_COUNT_SPLAY(bound_splay_insert); if (t != NULL) { t = splay(addr,t); if (compare(addr, t->start, t->size)==0) { @@ -1267,7 +1603,7 @@ static Tree * splay_insert(size_t addr, size_t size, Tree * t) #endif } if (new == NULL) { - bound_alloc_error(); + bound_alloc_error("not enough memory for bound checking code"); } else { if (t == NULL) { @@ -1294,13 +1630,14 @@ static Tree * splay_insert(size_t addr, size_t size, Tree * t) static Tree * splay_delete(size_t addr, Tree *t) /* Deletes addr from the tree if it's there. */ -/* Return a pointer to the resulting tree. */ +/* Return a pointer to the resulting tree. */ { Tree * x; + INCR_COUNT_SPLAY(bound_splay_delete); if (t==NULL) return NULL; t = splay(addr,t); - if (compare_destroy(addr, t->start) == 0) { /* found it */ + if (compare_destroy(addr, t->start) == 0) { /* found it */ if (t->left == NULL) { x = t->right; } else { @@ -1319,7 +1656,7 @@ static Tree * splay_delete(size_t addr, Tree *t) #endif return x; } else { - return t; /* It wasn't there */ + return t; /* It wasn't there */ } } @@ -1329,7 +1666,8 @@ void splay_printtree(Tree * t, int d) if (t == NULL) return; splay_printtree(t->right, d+1); for (i=0; istart, (unsigned long) t->size, (unsigned)t->is_invalid); + fprintf(stderr,"%p(0x%lx:%u:%u)\n", + (void *) t->start, (unsigned long) t->size, + (unsigned)t->type, (unsigned)t->is_invalid); splay_printtree(t->left, d+1); } diff --git a/tcc-doc.texi b/tcc-doc.texi index f700dd9c..0717bc3d 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -356,8 +356,10 @@ 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}. -There are four environment variables that can be used: +There are five environment variables that can be used: @table @option +@item TCC_BOUNDS_WARN_POINTER_ADD +Print warning when pointer add creates an illegal pointer. @item TCC_BOUNDS_PRINT_CALLS Print bound checking calls. Can be used for debugging. @item TCC_BOUNDS_PRINT_HEAP @@ -368,10 +370,7 @@ Print statistic information at exit of program. 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 -ba -Generate address checking tests when using @option{-b}. This will be a lot slower but finds more errors. +Note: @option{-b} is only available on i386 (linux and windows) and x86_64 (linux and windows) for the moment. @item -bt N Display N callers in stack traces. This is useful with @option{-g} or @@ -947,6 +946,40 @@ Here are some examples of caught errors: @end table +Signal handlers are not compatible with bounds checking. The code +below can be used to protect signal handlers. +The call to __bound_checking(1) will disable bounds checking in the +whole application. + +The BOUNDS_CHECKING_OFF and BOUNDS_CHECKING_ON can also be used to +disable bounds checking for some code. This is not recommended. +It is better to fix the code. + +@example + +#ifdef __BOUNDS_CHECKING_ON +extern void __bound_checking (int no_check); +#define BOUNDS_CHECKING_OFF __bound_checking(1) +#define BOUNDS_CHECKING_ON __bound_checking(-1) +#else +#define BOUNDS_CHECKING_OFF +#define BOUNDS_CHECKING_ON +#endif + +void real_signal_handler(int sig, siginfo_t *info, void *ucontext) +@{ + ... +@} + +void signal_handler(int sig, void *info, void *ucontext) +@{ + BOUNDS_CHECKING_OFF; + real_signal_handler(sig, info, data); + BOUNDS_CHECKING_ON; +@} + +@end example + @node Libtcc @chapter The @code{libtcc} library diff --git a/tcc.h b/tcc.h index 038a1234..eb46bbda 100644 --- a/tcc.h +++ b/tcc.h @@ -292,6 +292,9 @@ extern long double strtold (const char *__nptr, char **__endptr); #ifndef TCC_LIBTCC1 # define TCC_LIBTCC1 "libtcc1.a" #endif +#ifndef TCC_LIBBCHECK +# define TCC_LIBBCHECK "bcheck.o" +#endif /* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */ #if defined CONFIG_USE_LIBGCC && !defined TCC_LIBGCC @@ -1375,6 +1378,9 @@ ST_DATA CType func_vt; /* current function return type (used by return instructi ST_DATA int func_var; /* true if current function is variadic */ ST_DATA int func_vc; ST_DATA const char *funcname; +#ifdef CONFIG_TCC_BCHECK +ST_DATA addr_t func_bound_offset; +#endif ST_FUNC void tcc_debug_start(TCCState *s1); ST_FUNC void tcc_debug_end(TCCState *s1); @@ -1440,6 +1446,8 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty); #endif #ifdef CONFIG_TCC_BCHECK ST_FUNC void gbound_args(int nb_args); +ST_FUNC void save_temp_local(int nb_args); +ST_FUNC void restore_temp_local(void); #endif /* ------------ tccelf.c ------------ */ diff --git a/tccelf.c b/tccelf.c index 3d95a003..2577ac5b 100644 --- a/tccelf.c +++ b/tccelf.c @@ -85,6 +85,23 @@ ST_FUNC void tccelf_bounds_new(TCCState *s) lbounds_section = new_section(s, ".lbounds", SHT_PROGBITS, SHF_ALLOC); } + +ST_FUNC void tcc_add_bcheck(TCCState *s1) +{ + addr_t *ptr; + + if (0 == s1->do_bounds_check) + return; + ptr = section_ptr_add(bounds_section, sizeof(*ptr)); + *ptr = 0; + set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + bounds_section->sh_num, "__bounds_start"); + /* pull bcheck.o from libtcc1.a */ + set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_UNDEF, "__bound_init"); +} #endif ST_FUNC void tccelf_stab_new(TCCState *s) @@ -1288,7 +1305,7 @@ static int tcc_add_support(TCCState *s1, const char *filename) } #endif -static void add_array (const char *section, TCCState *s1, Sym *sym) +static void add_array (const char *section, TCCState *s1, Sym *sym, int sh_type) { Section *s; unsigned char *ptr; @@ -1296,6 +1313,9 @@ static void add_array (const char *section, TCCState *s1, Sym *sym) s = find_section(s1, section); if (s) { s->sh_flags |= SHF_WRITE; +#ifndef TCC_TARGET_PE + s->sh_type = sh_type; +#endif ptr = section_ptr_add(s, PTR_SIZE); memset (ptr, 0, PTR_SIZE); put_elf_reloc (s1->symtab, s, ptr - s->data, R_DATA_PTR, sym->c); @@ -1304,12 +1324,12 @@ static void add_array (const char *section, TCCState *s1, Sym *sym) ST_FUNC void add_init_array (TCCState *s1, Sym *sym) { - add_array (".init_array", s1, sym); + add_array (".init_array", s1, sym, SHT_INIT_ARRAY); } ST_FUNC void add_fini_array (TCCState *s1, Sym *sym) { - add_array (".fini_array", s1, sym); + add_array (".fini_array", s1, sym, SHT_FINI_ARRAY); } /* add tcc runtime libraries */ @@ -1336,7 +1356,7 @@ ST_FUNC void tcc_add_runtime(TCCState *s1) if (s1->do_bounds_check && s1->output_type != TCC_OUTPUT_DLL) { tcc_add_library_err(s1, "pthread"); tcc_add_library_err(s1, "dl"); - tcc_add_support(s1, "bcheck.o"); + tcc_add_support(s1, TCC_LIBBCHECK); } #endif tcc_add_support(s1, TCC_LIBTCC1); @@ -2040,6 +2060,7 @@ static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, Section *s; ElfW(Ehdr) ehdr; ElfW(Shdr) shdr, *sh; + ElfW(Sym) *sym; file_type = s1->output_type; shnum = s1->nb_sections; @@ -2110,6 +2131,10 @@ static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); sort_syms(s1, symtab_section); + if (s1->dynsym) + for_each_elem(s1->dynsym, 0, sym, ElfW(Sym)) + if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) + s1->dynsym->sh_info++; for(i = 1; i < s1->nb_sections; i++) { s = s1->sections[sec_order[i]]; if (s->sh_type != SHT_NOBITS) { diff --git a/tccgen.c b/tccgen.c index 0f3d96ec..990012c6 100644 --- a/tccgen.c +++ b/tccgen.c @@ -110,6 +110,16 @@ ST_DATA struct temp_local_variable { short align; } arr_temp_local_vars[MAX_TEMP_LOCAL_VARIABLE_NUMBER]; short nb_temp_local_vars; +#ifdef CONFIG_TCC_BCHECK +static short call_nesting; +static char used_location[MAX_TEMP_LOCAL_VARIABLE_NUMBER]; +static int nb_bound_local_param; +struct { + unsigned long data_offset; + int v; +} *bound_local_param; +ST_DATA addr_t func_bound_offset; +#endif static struct scope { struct scope *prev; @@ -1383,11 +1393,6 @@ ST_FUNC void save_reg_upstack(int r, int n) bt = VT_PTR; sv.type.t = bt; size = type_size(&sv.type, &align); -#ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check) - l = loc = (loc - size) & -align; - else -#endif l = get_temp_local_var(size,align); sv.r = VT_LOCAL | VT_LVAL; sv.c.i = l; @@ -1496,7 +1501,11 @@ static int get_temp_local_var(int size,int align){ found=0; for(i=0;isizealign){ + if(temp_var->sizealign +#ifdef CONFIG_TCC_BCHECK + || (tcc_state->do_bounds_check && used_location[i]) +#endif + ){ continue; } /*check if temp_var is free*/ @@ -1596,6 +1605,50 @@ ST_FUNC void gbound_args(int nb_args) vrott(i); } } + +ST_FUNC void save_temp_local(int nb_args) +{ + int i, j; + + if (call_nesting++ == 0) + for (i = 1; i <= nb_args; ++i) + for (j = 0; j < nb_temp_local_vars; j++) + if (vtop[1 - i].c.i == arr_temp_local_vars[j].location) { + used_location[j] = 1; + break; + } +} + +ST_FUNC void restore_temp_local() +{ + if (--call_nesting == 0) + memset (used_location, 0, sizeof (used_location)); +} + +static void add_bound_param(CType *type, int size, int v, int c) +{ + addr_t *bounds_ptr; + /* Add arrays/structs/unions because we always take address */ + int taken = (type->t & VT_ARRAY) + || (type->t & VT_BTYPE) == VT_STRUCT; + + if (taken == 0) { + /* Add parameter to check */ + nb_bound_local_param++; + bound_local_param = + tcc_realloc (bound_local_param, + nb_bound_local_param * + sizeof (*bound_local_param)); + bound_local_param[nb_bound_local_param-1].data_offset = + lbounds_section->data_offset; + bound_local_param[nb_bound_local_param-1].v = v; + } + /* add local bound info */ + bounds_ptr = section_ptr_add(lbounds_section, + 2 * sizeof(addr_t)); + bounds_ptr[0] = c; + bounds_ptr[1] = taken ? size : ~size; +} #endif static void incr_bf_adr(int o) @@ -3418,6 +3471,7 @@ ST_FUNC void vstore(void) vswap(); /* source */ + vtop->r &= ~VT_MUSTBOUND; vpushv(vtop - 2); vtop->type.t = VT_PTR; gaddrof(); @@ -5083,6 +5137,23 @@ ST_FUNC void unary(void) if ((vtop->type.t & VT_BTYPE) != VT_FUNC && !(vtop->type.t & VT_ARRAY)) test_lvalue(); +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check && vtop->sym) { + int i; + + /* Mark parameter as being used for address off */ + for (i = 0; i < nb_bound_local_param; i++) { + if (bound_local_param[i].v == vtop->sym->v) { + addr_t *bounds_ptr = + (addr_t *) (lbounds_section->data + + bound_local_param[i].data_offset); + bounds_ptr[1] = ~bounds_ptr[1]; + bound_local_param[i].v = 0; + break; + } + } + } +#endif mk_pointer(&vtop->type); gaddrof(); break; @@ -7435,8 +7506,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, if ((r & VT_VALMASK) == VT_LOCAL) { sec = NULL; #ifdef CONFIG_TCC_BCHECK - if (bcheck && ((type->t & VT_ARRAY) || - (type->t & VT_BTYPE) == VT_STRUCT)) { + if (bcheck && v) { + /* add padding between stack variables */ loc--; } #endif @@ -7444,17 +7515,10 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, addr = loc; #ifdef CONFIG_TCC_BCHECK /* handles bounds */ - /* XXX: currently, since we do only one pass, we cannot track - '&' operators, so we add only arrays/structs/unions */ - if (bcheck && ((type->t & VT_ARRAY) || - (type->t & VT_BTYPE) == VT_STRUCT)) { - addr_t *bounds_ptr; - /* add padding between regions */ + if (bcheck && v) { + add_bound_param (type, size, v, addr); + /* add padding between stack variables */ loc--; - /* then add local bound info */ - bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t)); - bounds_ptr[0] = addr; - bounds_ptr[1] = size; } #endif if (v) { @@ -7633,12 +7697,52 @@ static void gen_function(Sym *sym, AttributeDef *ad) sym_push2(&local_stack, SYM_FIELD, 0, 0); local_scope = 1; /* for function parameters */ gfunc_prolog(sym); +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check + && sym->type.ref->f.func_type != FUNC_ELLIPSIS) { + Sym *fpar; + + /* Add function arguments in case & is used */ + for (fpar = sym->type.ref->next; fpar; fpar = fpar->next) { + Sym *fsym = sym_find (fpar->v & ~SYM_FIELD); + + if (fsym && (fsym->r & VT_VALMASK) == VT_LOCAL) { + int align; + int size = type_size(&fsym->type, &align); + + if (size > 0) + add_bound_param (&fsym->type, size, fsym->v, fsym->c); + } + } + } +#endif local_scope = 0; rsym = 0; clear_temp_local_var_list(); block(0); gsym(rsym); nocode_wanted = 0; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) { + addr_t o = func_bound_offset; + + /* Remove parameters where address off is not used */ + while (o != lbounds_section->data_offset) { + addr_t *bounds_ptr = (addr_t *) (lbounds_section->data + o); + if ((ssize_t) bounds_ptr[1] < 0) { + lbounds_section->data_offset -= 2 * sizeof (addr_t); + memmove(bounds_ptr, bounds_ptr + 2, + lbounds_section->data_offset - o); + } + else { + o += 2 * sizeof (addr_t); + } + } + tcc_free (bound_local_param); + nb_bound_local_param = 0; + bound_local_param = NULL; + } +#endif gfunc_epilog(); cur_text_section->data_offset = ind; /* reset local stack */ diff --git a/tccpe.c b/tccpe.c index 9eb0b42a..dff6169b 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1897,7 +1897,7 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe) const char **pp, *p; #ifdef TCC_IS_NATIVE if (s1->do_bounds_check) - tcc_add_support(s1, "bcheck.o"); + tcc_add_support(s1, TCC_LIBBCHECK); #endif tcc_add_support(s1, TCC_LIBTCC1); for (pp = libs; 0 != (p = *pp); ++pp) { diff --git a/tccrun.c b/tccrun.c index 46abbaeb..494dd59f 100644 --- a/tccrun.c +++ b/tccrun.c @@ -86,6 +86,7 @@ LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr) dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size); dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx); ptr_diff = (char*)prx - (char*)ptr; + close(fd); } #else ptr = tcc_malloc(size); @@ -119,6 +120,17 @@ ST_FUNC void tcc_run_free(TCCState *s1) /* launch the compiled program with the given arguments */ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) { +/* PE target overwrites runtime_main */ +#ifndef TCC_TARGET_PE + typedef void (*init_array_func)(int, char **, char **); + typedef void (*fini_array_func)(void); + init_array_func *__init_array_start; + init_array_func *__init_array_end; + fini_array_func *__fini_array_start; + fini_array_func *__fini_array_end; +#endif + int i; + int ret; int (*prog_main)(int, char **); s1->runtime_main = s1->nostdlib ? "_start" : "main"; @@ -140,31 +152,34 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) errno = 0; /* clean errno value */ #ifdef CONFIG_TCC_BCHECK - if (s1->do_bounds_check) { - void (*bound_init)(void); - void (*bound_exit)(void); - void (*bounds_add_static_var)(size_t *p); - size_t *bounds_start; - int ret; - + if (s1->do_bounds_check) /* set error function */ s1->rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg"); - /* XXX: use .init section so that it also work in binary ? */ - bound_init = tcc_get_symbol_err(s1, "__bound_init"); - bound_exit = tcc_get_symbol_err(s1, "__bound_exit"); - bounds_add_static_var = tcc_get_symbol_err(s1, "__bounds_add_static_var"); - bounds_start = tcc_get_symbol_err(s1, "__bounds_start"); +#endif - bound_init(); - bounds_add_static_var (bounds_start); +#ifndef TCC_TARGET_PE + __init_array_start = tcc_get_symbol_err(s1, "__init_array_start"); + __init_array_end = tcc_get_symbol_err(s1, "__init_array_end"); + __fini_array_start = tcc_get_symbol_err(s1, "__fini_array_start"); + __fini_array_end = tcc_get_symbol_err(s1, "__fini_array_end"); - ret = (*prog_main)(argc, argv); - - bound_exit(); - return ret; + if (__init_array_start && __init_array_end) { + i = 0; + while (&__init_array_start[i] != __init_array_end) + (*__init_array_start[i++])(argc, argv, environ); } #endif - return (*prog_main)(argc, argv); + + ret = (*prog_main)(argc, argv); + +#ifndef TCC_TARGET_PE + if (__fini_array_start && __fini_array_end) { + i = 0; + while (&__fini_array_end[i] != __fini_array_start) + (*__fini_array_end[--i])(); + } +#endif + return ret; } #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 @@ -649,6 +664,9 @@ static void set_exception_handler(void) /* install TCC signal handlers to print debug info on fatal runtime errors */ sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; +#ifdef SIGSTKSZ + sigact.sa_flags |= SA_ONSTACK; +#endif sigact.sa_sigaction = sig_error; sigemptyset(&sigact.sa_mask); sigaction(SIGFPE, &sigact, NULL); @@ -656,6 +674,18 @@ static void set_exception_handler(void) sigaction(SIGSEGV, &sigact, NULL); sigaction(SIGBUS, &sigact, NULL); sigaction(SIGABRT, &sigact, NULL); +#ifdef SIGSTKSZ + /* This allows stack overflow to be reported instead of a SEGV */ + { + stack_t ss; + static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16))); + + ss.ss_sp = stack; + ss.ss_size = SIGSTKSZ; + ss.ss_flags = 0; + sigaltstack(&ss, NULL); + } +#endif } #else /* WIN32 */ diff --git a/tests/Makefile b/tests/Makefile index 034ddc16..c4790606 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -168,7 +168,7 @@ memtest: # memory and bound check auto test -BOUNDS_OK = 1 4 8 10 14 16 +BOUNDS_OK = 1 4 8 10 14 16 18 BOUNDS_FAIL= 2 5 6 7 9 11 12 13 15 17 btest: boundtest.c diff --git a/tests/boundtest.c b/tests/boundtest.c index a7ab800d..bd9ea5ba 100644 --- a/tests/boundtest.c +++ b/tests/boundtest.c @@ -230,6 +230,202 @@ int test17() return 0; } +#define CHECK(s) if (strstr (__bound_error_msg, s) == 0) abort(); + +extern void __bound_never_fatal (int neverfatal); +extern const char *__bound_error_msg; + +static void init18(char *a, char *b) +{ + memset (a, 'a', 10); + a[3] = 0; + a[9] = 0; + memset (b, 'b', 10); + __bound_error_msg = ""; +} + +/* ok (catch all errors) */ +int test18() +{ + char pad1[10]; + char a[10]; + char pad2[10]; + char b[10]; + char pad3[10]; + + memset (pad1, 0, sizeof(pad1)); + memset (pad2, 0, sizeof(pad2)); + memset (pad3, 0, sizeof(pad3)); + + /* -2 in case TCC_BOUNDS_NEVER_FATAL is set */ + __bound_never_fatal (-2); + + /* memcpy */ + init18(a,b); + memcpy(&a[1],&b[0],10); + CHECK("memcpy dest"); + init18(a,b); + memcpy(&a[0],&b[1],10); + CHECK("memcpy src"); + init18(a,b); + memcpy(&a[0],&a[3],4); + CHECK("overlapping regions"); + init18(a,b); + memcpy(&a[3],&a[0],4); + CHECK("overlapping regions"); + + /* memcmp */ + init18(a,b); + memcmp(&b[1],&b[0],10); + CHECK("memcmp s1"); + init18(a,b); + memcmp(&b[0],&b[1],10); + CHECK("memcmp s2"); + + /* memmove */ + init18(a,b); + memmove(&b[1],&b[0],10); + CHECK("memmove dest"); + init18(a,b); + memmove(&b[0],&b[1],10); + CHECK("memmove src"); + + /* memset */ + init18(a,b); + memset(&b[1],'b',10); + CHECK("memset"); + + /* strlen */ + init18(a,b); + strlen(&b[0]); + CHECK("strlen"); + + /* strcpy */ + init18(a,b); + strcpy(&a[7], &a[0]); + CHECK("strcpy dest"); + init18(a,b); + strcpy(&a[0], &b[7]); + CHECK("strcpy src"); + init18(a,b); + strcpy(&a[0], &a[1]); + CHECK("overlapping regions"); + init18(a,b); + strcpy(&a[2], &a[0]); + CHECK("overlapping regions"); + + /* strncpy */ + init18(a,b); + strncpy(&a[7], &a[0], 10); + CHECK("strncpy dest"); + init18(a,b); + strncpy(&a[0], &b[7], 10); + CHECK("strncpy src"); + init18(a,b); + strncpy(&a[0], &a[1], 10); + CHECK("overlapping regions"); + strncpy(&a[2], &a[0], 10); + CHECK("overlapping regions"); + + /* strcmp */ + init18(a,b); + strcmp(&b[2], &b[0]); + CHECK("strcmp s1"); + init18(a,b); + strcmp(&b[0], &b[2]); + CHECK("strcmp s2"); + + /* strncmp */ + init18(a,b); + strncmp(&b[5], &b[0], 10); + CHECK("strncmp s1"); + init18(a,b); + strncmp(&b[0], &b[5], 10); + CHECK("strncmp s2"); + + /* strcat */ + init18(a,b); + strcat(&a[7], &a[0]); + CHECK("strcat dest"); + init18(a,b); + strcat(&a[0], &b[5]); + CHECK("strcat src"); + init18(a,b); + strcat(&a[0], &a[4]); + CHECK("overlapping regions"); + init18(a,b); + strcat(&a[3], &a[0]); + CHECK("overlapping regions"); + + /* strchr */ + init18(a,b); + strchr(&b[0], 'a'); + CHECK("strchr"); + + /* strdup */ + init18(a,b); + free(strdup(&b[0])); + CHECK("strdup"); + + __bound_never_fatal (2); + + /* memcpy */ + init18(a,b); + memcpy(&a[0],&b[0],10); + init18(a,b); + memcpy(&a[0],&a[3],3); + init18(a,b); + memcpy(&a[3],&a[0],3); + + /* memcmp */ + init18(a,b); + memcmp(&b[0],&b[0],10); + + /* memmove */ + init18(a,b); + memmove(&b[0],&b[5],5); + init18(a,b); + memmove(&b[5],&b[0],5); + + /* memset */ + init18(a,b); + memset(&b[0],'b',10); + + /* strlen */ + init18(a,b); + strlen (&a[0]); + + /* strcpy */ + init18(a,b); + strcpy (&a[0], &a[7]); + + /* strncpy */ + init18(a,b); + strncpy (&a[0], &a[7], 4); + + /* strcmp */ + init18(a,b); + strcmp (&a[0], &a[4]); + + /* strncmp */ + init18(a,b); + strncmp (&a[0], &a[4], 10); + + /* strcat */ + init18(a,b); + strcat (&a[0], &a[7]); + + /* strchr */ + init18(a,b); + strchr (&a[0], 0); + + /* strdup */ + init18(a,b); + free (strdup (&a[0])); + + return 0; +} + int (*table_test[])(void) = { test1, test2, @@ -248,6 +444,7 @@ int (*table_test[])(void) = { test15, test16, test17, + test18, }; int main(int argc, char **argv) diff --git a/x86_64-gen.c b/x86_64-gen.c index 439fd988..b4a546c2 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -637,7 +637,6 @@ static void gcall_or_jmp(int is_jmp) } #if defined(CONFIG_TCC_BCHECK) -static addr_t func_bound_offset; static unsigned long func_bound_ind; static void gen_bounds_call(int v) @@ -784,8 +783,10 @@ void gfunc_call(int nb_args) int arg; #ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check) + if (tcc_state->do_bounds_check) { + save_temp_local (nb_args); gbound_args(nb_args); + } #endif args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE; @@ -906,6 +907,10 @@ void gfunc_call(int nb_args) } vtop--; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + restore_temp_local (); +#endif } @@ -1271,8 +1276,10 @@ void gfunc_call(int nb_args) char _onstack[nb_args ? nb_args : 1], *onstack = _onstack; #ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check) + if (tcc_state->do_bounds_check) { + save_temp_local (nb_args); gbound_args(nb_args); + } #endif /* calculate the number of integer/float register arguments, remember @@ -1453,6 +1460,10 @@ void gfunc_call(int nb_args) if (args_size) gadd_sp(args_size); vtop--; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) + restore_temp_local (); +#endif } #define FUNC_PROLOG_SIZE 11 diff --git a/x86_64-link.c b/x86_64-link.c index 0730c2db..dcb2b90c 100644 --- a/x86_64-link.c +++ b/x86_64-link.c @@ -287,89 +287,4 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t } } -#ifdef CONFIG_TCC_BCHECK -ST_FUNC void tcc_add_bcheck(TCCState *s1) -{ - addr_t *ptr; - int loc_glob; - int sym_index; - int bsym_index; - - if (0 == s1->do_bounds_check) - return; - /* XXX: add an object file to do that */ - ptr = section_ptr_add(bounds_section, sizeof(*ptr)); - *ptr = 0; - loc_glob = s1->output_type != TCC_OUTPUT_MEMORY ? STB_LOCAL : STB_GLOBAL; - bsym_index = set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(loc_glob, STT_NOTYPE), 0, - bounds_section->sh_num, "__bounds_start"); - /* pull bcheck.o from libtcc1.a */ - sym_index = set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - SHN_UNDEF, "__bound_init"); - if (s1->output_type != TCC_OUTPUT_MEMORY) { - /* add 'call __bound_init()' in .init section */ - Section *init_section = find_section(s1, ".init"); - unsigned char *pinit; -#ifdef TCC_TARGET_PE - pinit = section_ptr_add(init_section, 8); - pinit[0] = 0x55; /* push %rbp */ - pinit[1] = 0x48; /* mov %rsp,%rpb */ - pinit[2] = 0x89; - pinit[3] = 0xe5; - pinit[4] = 0x48; /* sub $0x10,%rsp */ - pinit[5] = 0x83; - pinit[6] = 0xec; - pinit[7] = 0x10; -#endif - pinit = section_ptr_add(init_section, 5); - pinit[0] = 0xe8; - write32le(pinit + 1, -4); - put_elf_reloc(symtab_section, init_section, - init_section->data_offset - 4, R_386_PC32, sym_index); - /* R_386_PC32 = R_X86_64_PC32 = 2 */ - pinit = section_ptr_add(init_section, 13); - pinit[0] = 0x48; /* mov xx,%rax */ - pinit[1] = 0xb8; - write64le(pinit + 2, 0); -#ifdef TCC_TARGET_PE - pinit[10] = 0x48; /* mov %rax,%rcx */ - pinit[11] = 0x89; - pinit[12] = 0xc1; -#else - pinit[10] = 0x48; /* mov %rax,%rdi */ - pinit[11] = 0x89; - pinit[12] = 0xc7; -#endif - put_elf_reloc(symtab_section, init_section, - init_section->data_offset - 11, R_X86_64_64, bsym_index); - sym_index = set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - SHN_UNDEF, "__bounds_add_static_var"); - pinit = section_ptr_add(init_section, 5); - pinit[0] = 0xe8; - write32le(pinit + 1, -4); - put_elf_reloc(symtab_section, init_section, - init_section->data_offset - 4, R_386_PC32, sym_index); - /* R_386_PC32 = R_X86_64_PC32 = 2 */ -#ifdef TCC_TARGET_PE - { - int init_index = set_elf_sym(symtab_section, - 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - init_section->sh_num, "__init_start"); - Sym sym; - init_section->sh_flags |= SHF_EXECINSTR; - pinit = section_ptr_add(init_section, 2); - pinit[0] = 0xc9; /* leave */ - pinit[1] = 0xc3; /* ret */ - sym.c = init_index; - add_init_array (s1, &sym); - } -#endif - } -} -#endif - #endif /* !TARGET_DEFS_ONLY */