diff --git a/lib/Makefile b/lib/Makefile index ae37cc44..2a9d7abe 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -42,7 +42,7 @@ ifdef CONFIG_OSX XFLAGS += -D_ANSI_SOURCE endif -XFLAGS += -g -Wno-deprecated-declarations +XFLAGS += -g I386_O = libtcc1.o alloca86.o alloca86-bt.o X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o diff --git a/lib/bcheck.c b/lib/bcheck.c index bb716418..9a40f15c 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -35,6 +35,7 @@ #endif #define BOUND_DEBUG +#define BOUND_STATISTIC #ifdef BOUND_DEBUG #define dprintf(a...) if (print_calls) fprintf(a) @@ -50,8 +51,7 @@ || defined(__DragonFly__) \ || defined(__OpenBSD__) \ || defined(__NetBSD__) \ - || defined(__dietlibc__) \ - || defined(_WIN32) + || defined(__dietlibc__) #undef HAVE_MEMALIGN #define INIT_SEM() #define EXIT_SEM() @@ -59,6 +59,16 @@ #define POST_SEM() #define HAS_ENVIRON 0 #define MALLOC_REDIR (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) #else #include #include @@ -86,6 +96,7 @@ static unsigned char initial_pool[256]; #define TCC_TYPE_CALLOC (2) #define TCC_TYPE_REALLOC (3) #define TCC_TYPE_MEMALIGN (4) +#define TCC_TYPE_STRDUP (5) /* this pointer is generated when bound check is incorrect */ #define INVALID_POINTER ((void *)(-2)) @@ -149,9 +160,45 @@ static alloca_list_type *alloca_list = NULL; 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; +#ifdef BOUND_STATISTIC +static unsigned long bound_ptr_add_count; +static unsigned long bound_ptr_indir1_count; +static unsigned long bound_ptr_indir2_count; +static unsigned long bound_ptr_indir4_count; +static unsigned long bound_ptr_indir8_count; +static unsigned long bound_ptr_indir12_count; +static unsigned long bound_ptr_indir16_count; +static unsigned long bound_local_new_count; +static unsigned long bound_local_delete_count; +static unsigned long bound_malloc_count; +static unsigned long bound_calloc_count; +static unsigned long bound_realloc_count; +static unsigned long bound_free_count; +static unsigned long bound_memalign_count; +static unsigned long bound_mmap_count; +static unsigned long bound_munmap_count; +static unsigned long bound_alloca_count; +static unsigned long bound_mempcy_count; +static unsigned long bound_memcmp_count; +static unsigned long bound_memmove_count; +static unsigned long bound_memset_count; +static unsigned long bound_strlen_count; +static unsigned long bound_strcpy_count; +static unsigned long bound_strncpy_count; +static unsigned long bound_strcmp_count; +static unsigned long bound_strncmp_count; +static unsigned long bound_strcat_count; +static unsigned long bound_strchr_count; +static unsigned long bound_strdup_count; +#define INCR_COUNT(x) x++ +#else +#define INCR_COUNT(x) +#endif + /* enable/disable checking. This can be used for signal handlers. */ void __bound_checking (int no_check) { @@ -186,6 +233,7 @@ void * FASTCALL __bound_ptr_add(void *p, size_t offset) __FILE__, __FUNCTION__, p, (unsigned)offset); WAIT_SEM (); + INCR_COUNT(bound_ptr_add_count); if (tree) { tree = splay (addr, tree); addr -= tree->start; @@ -223,6 +271,7 @@ void * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \ dprintf(stderr, "%s %s: %p 0x%x start\n", \ __FILE__, __FUNCTION__, p, (unsigned)offset); \ WAIT_SEM (); \ + INCR_COUNT(bound_ptr_indir ## dsize ## _count); \ if (tree) { \ tree = splay (addr, tree); \ addr -= tree->start; \ @@ -284,6 +333,7 @@ void FASTCALL __bound_local_new(void *p1) addr = p[0]; if (addr == 0) break; + INCR_COUNT(bound_local_new_count); if (addr == 1) { dprintf(stderr, "%s, %s() alloca/vla used\n", __FILE__, __FUNCTION__); @@ -315,6 +365,7 @@ void FASTCALL __bound_local_delete(void *p1) addr = p[0]; if (addr == 0) break; + INCR_COUNT(bound_local_delete_count); if (addr == 1) { while (alloca_list && alloca_list->fp == fp) { alloca_list_type *next = alloca_list->next; @@ -350,6 +401,7 @@ void __bound_init(void) 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__); @@ -444,7 +496,7 @@ void __attribute__((destructor)) __bound_exit(void) { int i; static const char * const alloc_type[] = { - "", "malloc", "calloc", "realloc", "memalign" + "", "malloc", "calloc", "realloc", "memalign", "strdup" }; dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__); @@ -488,6 +540,39 @@ void __attribute__((destructor)) __bound_exit(void) } EXIT_SEM (); inited = 0; +#ifdef BOUND_STATISTIC + if (print_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); + fprintf (stderr, "bound_ptr_indir4_count %llu\n", bound_ptr_indir4_count); + fprintf (stderr, "bound_ptr_indir8_count %llu\n", bound_ptr_indir8_count); + fprintf (stderr, "bound_ptr_indir12_count %llu\n", bound_ptr_indir12_count); + fprintf (stderr, "bound_ptr_indir16_count %llu\n", bound_ptr_indir16_count); + fprintf (stderr, "bound_local_new_count %llu\n", bound_local_new_count); + fprintf (stderr, "bound_local_delete_count %llu\n", bound_local_delete_count); + fprintf (stderr, "bound_malloc_count %llu\n", bound_malloc_count); + fprintf (stderr, "bound_calloc_count %llu\n", bound_calloc_count); + fprintf (stderr, "bound_realloc_count %llu\n", bound_realloc_count); + fprintf (stderr, "bound_free_count %llu\n", bound_free_count); + fprintf (stderr, "bound_memalign_count %llu\n", bound_memalign_count); + fprintf (stderr, "bound_mmap_count %llu\n", bound_mmap_count); + fprintf (stderr, "bound_munmap_count %llu\n", bound_munmap_count); + fprintf (stderr, "bound_alloca_count %llu\n", bound_alloca_count); + fprintf (stderr, "bound_mempcy_count %llu\n", bound_mempcy_count); + fprintf (stderr, "bound_memcmp_count %llu\n", bound_memcmp_count); + fprintf (stderr, "bound_memmove_count %llu\n", bound_memmove_count); + fprintf (stderr, "bound_memset_count %llu\n", bound_memset_count); + fprintf (stderr, "bound_strlen_count %llu\n", bound_strlen_count); + fprintf (stderr, "bound_strcpy_count %llu\n", bound_strcpy_count); + fprintf (stderr, "bound_strncpy_count %llu\n", bound_strncpy_count); + fprintf (stderr, "bound_strcmp_count %llu\n", bound_strcmp_count); + fprintf (stderr, "bound_strncmp_count %llu\n", bound_strncmp_count); + 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); + } +#endif } } @@ -516,6 +601,7 @@ void *__bound_malloc(size_t size, const void *caller) 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); #else @@ -544,6 +630,7 @@ 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 (align > 4) { @@ -599,6 +686,7 @@ void __bound_free(void *ptr, const void *caller) 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) { @@ -645,6 +733,7 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller) ptr = realloc (ptr, size); #endif WAIT_SEM (); + INCR_COUNT(bound_realloc_count); if (ptr) { tree = splay_insert ((size_t) ptr, size, tree); if (tree->start == (size_t) ptr) { @@ -701,6 +790,7 @@ void *__bound_calloc(size_t nmemb, size_t size) if (ptr) { memset (ptr, 0, size); WAIT_SEM (); + INCR_COUNT(bound_calloc_count); tree = splay_insert ((size_t) ptr, size, tree); if (tree->start == (size_t) ptr) { tree->type = TCC_TYPE_CALLOC; @@ -721,6 +811,7 @@ void *__bound_mmap (void *start, size_t size, int prot, result = mmap (start, size, prot, flags, fd, offset); if (result) { WAIT_SEM (); + INCR_COUNT(bound_mmap_count); tree = splay_insert((size_t)result, size, tree); POST_SEM (); } @@ -734,6 +825,7 @@ int __bound_munmap (void *start, size_t size) 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 (); result = munmap (start, size); @@ -751,6 +843,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 (); + INCR_COUNT(bound_alloca_count); GET_CALLER_FP (fp); last = NULL; cur = alloca_list; @@ -813,6 +906,7 @@ void *__bound_memcpy(void *dst, const void *src, size_t size) { void* p; + INCR_COUNT(bound_mempcy_count); __bound_check(dst, size); __bound_check(src, size); /* check also region overlap */ @@ -824,8 +918,17 @@ void *__bound_memcpy(void *dst, const void *src, size_t size) return p; } +int __bound_memcmp(const void *s1, const void *s2, size_t size) +{ + INCR_COUNT(bound_memcmp_count); + __bound_check(s1, size); + __bound_check(s2, size); + return memcmp(s1, s2, size); +} + void *__bound_memmove(void *dst, const void *src, size_t size) { + INCR_COUNT(bound_memmove_count); __bound_check(dst, size); __bound_check(src, size); return memmove(dst, src, size); @@ -833,25 +936,22 @@ void *__bound_memmove(void *dst, const void *src, size_t size) void *__bound_memset(void *dst, int c, size_t size) { + INCR_COUNT(bound_memset_count); __bound_check(dst, size); return memset(dst, c, size); } -/* XXX: could be optimized */ int __bound_strlen(const char *s) { - const char *p; + const char *p = s; size_t len; - len = 0; - for(;;) { - p = __bound_ptr_indir1((char *)s, len); - if (p == INVALID_POINTER) - bound_error("bad pointer in strlen()"); - if (*p == '\0') - break; - len++; - } + 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; } @@ -860,11 +960,101 @@ char *__bound_strcpy(char *dst, const char *src) size_t len; void *p; + INCR_COUNT(bound_strcpy_count); len = __bound_strlen(src); p = __bound_memcpy(dst, src, len + 1); return p; } +char *__bound_strncpy(char *dst, const char *src, size_t n) +{ + size_t len; + void *p; + + INCR_COUNT(bound_strncpy_count); + __bound_check(dst, n); + __bound_check(src, n); + return strncpy (dst, src, n); +} + +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; + + INCR_COUNT(bound_strcmp_count); + while (*u1 && *u1 == *u2) { + u1++; + u2++; + } + __bound_check(s1, ((const char *)u1 - s1) + 1); + __bound_check(s2, ((const char *)u2 - s2) + 1); + return (*u1 - *u2); +} + +int __bound_strncmp(const char *s1, const char *s2, size_t n) +{ + INCR_COUNT(bound_strncmp_count); + __bound_check(s1, n); + __bound_check(s2, n); + return strncmp(s1, s2, n); +} + +char *__bound_strcat(char *dest, const char *src) +{ + char *r = dest; + const char *s = src; + + INCR_COUNT(bound_strcat_count); + while (*dest++); + dest--; + while ((*dest++ = *src++) != 0); + __bound_check(r, dest - r); + __bound_check(s, src - s); + return r; +} + +char *__bound_strchr(const char *string, int ch) +{ + const unsigned char *s = (const unsigned char *) string; + unsigned char c = ch; + + INCR_COUNT(bound_strchr_count); + while (*s) { + if (*s == c) { + break; + } + s++; + } + __bound_check(string, ((const char *)s - string) + 1); + return *s == c ? (char *) s : NULL; +} + +char *__bound_strdup(const char *s) +{ + const char *p = s; + char *new; + + INCR_COUNT(bound_strdup_count); + while (*p++); + __bound_check(s, p - s); +#if MALLOC_REDIR + new = malloc_redir (p - s); +#else + new = malloc (p - s); +#endif + if (new) { + WAIT_SEM (); + tree = splay_insert((size_t)new, p - s, tree); + if (tree->start == (size_t) new) { + tree->type = TCC_TYPE_STRDUP; + } + memcpy (new, s, p - s); + POST_SEM (); + } + return new; +} + /* An implementation of top-down splaying with sizes D. Sleator , January 1994. diff --git a/tcc-doc.texi b/tcc-doc.texi index 4409546f..f700dd9c 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -356,12 +356,14 @@ 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 three environment variables that can be used: +There are four 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_PRINT_STATISTIC +Print statistic information at exit of program. @item TCC_BOUNDS_NEVER_FATAL Try to continue in case of a bound checking error. @end table @@ -915,7 +917,7 @@ Here are some examples of caught errors: int *tab; tab = malloc(20 * sizeof(int)); for(i=0;i<21;i++) @{ - sum += tab4[i]; + sum += tab[i]; @} free(tab); @} @@ -928,7 +930,7 @@ Here are some examples of caught errors: tab = malloc(20 * sizeof(int)); free(tab); for(i=0;i<20;i++) @{ - sum += tab4[i]; + sum += tab[i]; @} @} @end example diff --git a/tccgen.c b/tccgen.c index 038a5c64..427e7734 100644 --- a/tccgen.c +++ b/tccgen.c @@ -432,8 +432,15 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num, case TOK_memcpy: case TOK_memmove: case TOK_memset: + case TOK_memcmp: case TOK_strlen: case TOK_strcpy: + case TOK_strncpy: + case TOK_strcmp: + case TOK_strncmp: + case TOK_strcat: + case TOK_strchr: + case TOK_strdup: case TOK_alloca: case TOK_mmap: case TOK_munmap: diff --git a/tcctok.h b/tcctok.h index 88d69196..6de1c6e9 100644 --- a/tcctok.h +++ b/tcctok.h @@ -314,8 +314,15 @@ # endif DEF(TOK_mmap, "mmap") DEF(TOK_munmap, "munmap") + DEF(TOK_memcmp, "memcmp") DEF(TOK_strlen, "strlen") DEF(TOK_strcpy, "strcpy") + DEF(TOK_strncpy, "strncpy") + DEF(TOK_strcmp, "strcmp") + DEF(TOK_strncmp, "strncmp") + DEF(TOK_strcat, "strcat") + DEF(TOK_strchr, "strchr") + DEF(TOK_strdup, "strdup") #endif /* Tiny Assembler */ diff --git a/tests/Makefile b/tests/Makefile index 1cfc0c0c..e8507f58 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -23,13 +23,12 @@ TESTS = \ tests2-dir \ pp-dir -BTESTS = test1b test3b btest +BTESTS = test1b test3b btest test4 -# test4 -- problem with -static +# test4_static -- Not all relocation types are implemented yet. # asmtest / asmtest2 -- minor differences with gcc -# btest -- works on i386 (including win32) -# bounds-checking is supported only on i386 +# bounds-checking is supported on i386 and x86_64 on linux and windows ifeq ($(ARCH),i386) TESTS += $(BTESTS) endif @@ -116,7 +115,7 @@ test3 test3b: tcctest.c test.ref $(TCC) $(RUN_TCC) $(RUN_TCC) $(RUN_TCC) -run $< > test.out3 @diff -u test.ref test.out3 && echo "Auto Test3 OK" -test%b : TCCFLAGS += -b +test%b : TCCFLAGS += -b -ba # binary output test test4: tcctest.c test.ref @@ -131,10 +130,12 @@ test4: tcctest.c test.ref ./tcctest1 > test1.out @if diff -u test.ref test1.out ; then echo "Dynamic Auto Test OK"; fi # dynamic output + bound check - $(TCC) -b -o tcctest4 $< + $(TCC) -b -ba -o tcctest4 $< ./tcctest4 > test4.out @if diff -u test.ref test4.out ; then echo "BCheck Auto Test OK"; fi -# static output + +test4_static: tcctest.c test.ref +# static output. $(TCC) -static -o tcctest2 $< ./tcctest2 > test2.out @if diff -u test.ref test2.out ; then echo "Static Auto Test OK"; fi @@ -162,8 +163,7 @@ memtest: # memory and bound check auto test -# 3 is profiling test -BOUNDS_OK = 1 4 8 10 14 16 +BOUNDS_OK = 1 3 4 8 10 14 16 BOUNDS_FAIL= 2 5 6 7 9 11 12 13 15 17 btest: boundtest.c @@ -171,7 +171,7 @@ btest: boundtest.c @ulimit -c 0; \ for i in $(BOUNDS_OK); do \ echo ; echo --- boundtest $$i ---; \ - if $(TCC) -b -run $< $$i ; then \ + if $(TCC) -b -ba -run $< $$i ; then \ echo succeeded as expected; \ else\ echo Failed positive test $$i ; exit 1 ; \ @@ -179,7 +179,7 @@ btest: boundtest.c done ;\ for i in $(BOUNDS_FAIL); do \ echo ; echo --- boundtest $$i ---; \ - if $(TCC) -b -run $< $$i ; then \ + if $(TCC) -b -ba -run $< $$i ; then \ echo Failed negative test $$i ; exit 1 ;\ else\ echo failed as expected; \