tcc-doc.texi: - Document attribute bound_no_checking tcctok.h: - Add bound_no_checking attribute tcc.h: - Add no_bcheck function attribute tccgen.c: - Use function attribute no_bcheck in merge_funcattr/parse_attribute/gen_function bcheck.c: - Fix no_checking in __bound_new_region/__bound_free/__bound_check tests/tests2/114_bound_signal.c: - Fix code with new attribute bound_no_checking tests/tests2/103_implicit_memmove.c: - Fix memmove prototype
Unified · Split
| @@ -112,6 +112,12 @@ static sem_t bounds_sem; · 111 unmodified lines | |||
| 112 | #define WAIT_SEM() if (use_sem) while (sem_wait (&bounds_sem) < 0 \ | 112 | #define WAIT_SEM() if (use_sem) while (sem_wait (&bounds_sem) < 0 \ |
| 113 | && errno == EINTR) | 113 | && errno == EINTR) |
| 114 | #define POST_SEM() if (use_sem) sem_post (&bounds_sem) | 114 | #define POST_SEM() if (use_sem) sem_post (&bounds_sem) |
| 115 | #elif 0 | ||
| 116 | static pthread_mutex_t bounds_mtx; | ||
| 117 | #define INIT_SEM() pthread_mutex_init (&bounds_mtx, NULL) | ||
| 118 | #define EXIT_SEM() pthread_mutex_destroy (&bounds_mtx) | ||
| 119 | #define WAIT_SEM() if (use_sem) pthread_mutex_lock (&bounds_mtx) | ||
| 120 | #define POST_SEM() if (use_sem) pthread_mutex_unlock (&bounds_mtx) | ||
| 115 | #else | 121 | #else |
| 116 | static pthread_spinlock_t bounds_spin; | 122 | static pthread_spinlock_t bounds_spin; |
| 117 | /* about 25% faster then semaphore. */ | 123 | /* about 25% faster then semaphore. */ |
| @@ -618,6 +624,9 @@ void __bound_new_region(void *p, size_t size) · 500 unmodified lines | |||
| 618 | alloca_list_type *cur; | 624 | alloca_list_type *cur; |
| 619 | alloca_list_type *new; | 625 | alloca_list_type *new; |
| 620 | 626 | ||
| 627 | if (no_checking) | ||
| 628 | return; | ||
| 629 | |||
| 621 | dprintf(stderr, "%s, %s(): %p, 0x%lx\n", | 630 | dprintf(stderr, "%s, %s(): %p, 0x%lx\n", |
| 622 | __FILE__, __FUNCTION__, p, (unsigned long)size); | 631 | __FILE__, __FUNCTION__, p, (unsigned long)size); |
| 623 | GET_CALLER_FP (fp); | 632 | GET_CALLER_FP (fp); |
| @@ -638,8 +647,7 @@ void __bound_new_region(void *p, size_t size) · 14 unmodified lines | |||
| 638 | last = cur; | 647 | last = cur; |
| 639 | cur = cur->next; | 648 | cur = cur->next; |
| 640 | } | 649 | } |
| 641 | if (no_checking == 0) | 650 | tree = splay_insert((size_t)p, size, tree); |
| 642 | tree = splay_insert((size_t)p, size, tree); | ||
| 643 | if (new) { | 651 | if (new) { |
| 644 | new->fp = fp; | 652 | new->fp = fp; |
| 645 | new->p = p; | 653 | new->p = p; |
| @@ -1188,7 +1196,7 @@ void __bound_free(void *ptr, const void *caller) · 542 unmodified lines | |||
| 1188 | size_t addr = (size_t) ptr; | 1196 | size_t addr = (size_t) ptr; |
| 1189 | void *p; | 1197 | void *p; |
| 1190 | 1198 | ||
| 1191 | if (ptr == NULL || tree == NULL || no_checking | 1199 | if (ptr == NULL || tree == NULL |
| 1192 | #if MALLOC_REDIR | 1200 | #if MALLOC_REDIR |
| 1193 | || ((unsigned char *) ptr >= &initial_pool[0] && | 1201 | || ((unsigned char *) ptr >= &initial_pool[0] && |
| 1194 | (unsigned char *) ptr < &initial_pool[sizeof(initial_pool)]) | 1202 | (unsigned char *) ptr < &initial_pool[sizeof(initial_pool)]) |
| @@ -1198,25 +1206,27 @@ void __bound_free(void *ptr, const void *caller) · 3 unmodified lines | |||
| 1198 | 1206 | ||
| 1199 | dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr); | 1207 | dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr); |
| 1200 | 1208 | ||
| 1201 | WAIT_SEM (); | 1209 | if (no_checking == 0) { |
| 1202 | INCR_COUNT(bound_free_count); | 1210 | WAIT_SEM (); |
| 1203 | tree = splay (addr, tree); | 1211 | INCR_COUNT(bound_free_count); |
| 1204 | if (tree->start == addr) { | 1212 | tree = splay (addr, tree); |
| 1205 | if (tree->is_invalid) { | 1213 | if (tree->start == addr) { |
| 1206 | POST_SEM (); | 1214 | if (tree->is_invalid) { |
| 1207 | bound_error("freeing invalid region"); | 1215 | POST_SEM (); |
| 1208 | return; | 1216 | bound_error("freeing invalid region"); |
| 1217 | return; | ||
| 1218 | } | ||
| 1219 | tree->is_invalid = 1; | ||
| 1220 | memset (ptr, 0x5a, tree->size); | ||
| 1221 | p = free_reuse_list[free_reuse_index]; | ||
| 1222 | free_reuse_list[free_reuse_index] = ptr; | ||
| 1223 | free_reuse_index = (free_reuse_index + 1) % FREE_REUSE_SIZE; | ||
| 1224 | if (p) | ||
| 1225 | tree = splay_delete((size_t)p, tree); | ||
| 1226 | ptr = p; | ||
| 1209 | } | 1227 | } |
| 1210 | tree->is_invalid = 1; | 1228 | POST_SEM (); |
| 1211 | memset (ptr, 0x5a, tree->size); | ||
| 1212 | p = free_reuse_list[free_reuse_index]; | ||
| 1213 | free_reuse_list[free_reuse_index] = ptr; | ||
| 1214 | free_reuse_index = (free_reuse_index + 1) % FREE_REUSE_SIZE; | ||
| 1215 | if (p) | ||
| 1216 | tree = splay_delete((size_t)p, tree); | ||
| 1217 | ptr = p; | ||
| 1218 | } | 1229 | } |
| 1219 | POST_SEM (); | ||
| 1220 | BOUND_FREE (ptr); | 1230 | BOUND_FREE (ptr); |
| 1221 | } | 1231 | } |
| 1222 | 1232 | ||
| @@ -1340,8 +1350,7 @@ int __bound_munmap (void *start, size_t size) · 117 unmodified lines | |||
| 1340 | /* check that (p ... p + size - 1) lies inside 'p' region, if any */ | 1350 | /* check that (p ... p + size - 1) lies inside 'p' region, if any */ |
| 1341 | static void __bound_check(const void *p, size_t size, const char *function) | 1351 | static void __bound_check(const void *p, size_t size, const char *function) |
| 1342 | { | 1352 | { |
| 1343 | if (no_checking == 0 && size != 0 && | 1353 | if (size != 0 && __bound_ptr_add((void *)p, size) == INVALID_POINTER) { |
| 1344 | __bound_ptr_add((void *)p, size) == INVALID_POINTER) { | ||
| 1345 | bound_error("invalid pointer %p, size 0x%lx in %s", | 1354 | bound_error("invalid pointer %p, size 0x%lx in %s", |
| 1346 | p, (unsigned long)size, function); | 1355 | p, (unsigned long)size, function); |
| 1347 | } | 1356 | } |
| @@ -951,34 +951,53 @@ Here are some examples of caught errors: · 950 unmodified lines | |||
| 951 | 951 | ||
| 952 | Signal handlers are not compatible with bounds checking. The code | 952 | Signal handlers are not compatible with bounds checking. The code |
| 953 | below can be used to protect signal handlers. | 953 | below can be used to protect signal handlers. |
| 954 | The call to __bound_checking(1) will disable bounds checking in the | 954 | The @code{__attribute__((bound_no_checking))} will prevent all bound checking |
| 955 | whole application. | 955 | code generation. If a signal handler calls another function this |
| 956 | function must also use @code{__attribute__((bound_no_checking))}. | ||
| 956 | 957 | ||
| 957 | The BOUNDS_CHECKING_OFF and BOUNDS_CHECKING_ON can also be used to | 958 | The fork() function call in a multi threaded application is also a problem. |
| 959 | To solve this all bounds checking can be disabled by calling | ||
| 960 | @code{__bound_checking(1)}. The call to @code{__bound_checking(1)} will disable bounds | ||
| 961 | checking in the whole application. | ||
| 962 | |||
| 963 | The @code{BOUNDS_CHECKING_OFF} and @code{BOUNDS_CHECKING_ON} can also be used to | ||
| 958 | disable bounds checking for some code. This is not recommended. | 964 | disable bounds checking for some code. This is not recommended. |
| 959 | It is better to fix the code. | 965 | It is better to fix the code. |
| 960 | 966 | ||
| 961 | @example | 967 | @example |
| 962 | 968 | ||
| 963 | #ifdef __BOUNDS_CHECKING_ON | 969 | #if defined(__TINYC__) && __BOUNDS_CHECKING_ON |
| 970 | #undef __attribute__ | ||
| 964 | extern void __bound_checking (int no_check); | 971 | extern void __bound_checking (int no_check); |
| 965 | #define BOUNDS_CHECKING_OFF __bound_checking(1) | 972 | #define BOUNDS_CHECKING_OFF __bound_checking(1) |
| 966 | #define BOUNDS_CHECKING_ON __bound_checking(-1) | 973 | #define BOUNDS_CHECKING_ON __bound_checking(-1) |
| 974 | #define BOUNDS_NO_CHECKING __attribute__((bound_no_checking)) | ||
| 967 | #else | 975 | #else |
| 968 | #define BOUNDS_CHECKING_OFF | 976 | #define BOUNDS_CHECKING_OFF |
| 969 | #define BOUNDS_CHECKING_ON | 977 | #define BOUNDS_CHECKING_ON |
| 978 | #define BOUNDS_NO_CHECKING | ||
| 970 | #endif | 979 | #endif |
| 971 | 980 | ||
| 972 | void real_signal_handler(int sig, siginfo_t *info, void *ucontext) | 981 | void signal_handler(int sig, void *info, void *ucontext) BOUNDS_NO_CHECKING |
| 973 | @{ | 982 | @{ |
| 974 | ... | 983 | ... signal handler code without generated bounds checking code. |
| 975 | @} | 984 | @} |
| 976 | 985 | ||
| 977 | void signal_handler(int sig, void *info, void *ucontext) | 986 | void run(const char *cmd) |
| 978 | @{ | 987 | @{ |
| 979 | BOUNDS_CHECKING_OFF; | 988 | switch (fork()) @{ |
| 980 | real_signal_handler(sig, info, data); | 989 | case 0: |
| 981 | BOUNDS_CHECKING_ON; | 990 | BOUNDS_CHECKING_OFF; |
| 991 | ... | ||
| 992 | exec... | ||
| 993 | exit(1); | ||
| 994 | case -1: | ||
| 995 | ... | ||
| 996 | break; | ||
| 997 | default: | ||
| 998 | ... | ||
| 999 | break; | ||
| 1000 | @} | ||
| 982 | @} | 1001 | @} |
| 983 | 1002 | ||
| 984 | @end example | 1003 | @end example |
| @@ -505,7 +505,8 @@ struct FuncAttr { · 504 unmodified lines | |||
| 505 | func_dtor : 1, /* attribute((destructor)) */ | 505 | func_dtor : 1, /* attribute((destructor)) */ |
| 506 | func_args : 8, /* PE __stdcall args */ | 506 | func_args : 8, /* PE __stdcall args */ |
| 507 | func_alwinl : 1, /* always_inline */ | 507 | func_alwinl : 1, /* always_inline */ |
| 508 | xxxx :15; | 508 | no_bcheck : 1, /* no bound checking */ |
| 509 | xxxx :14; | ||
| 509 | }; | 510 | }; |
| 510 | 511 | ||
| 511 | /* symbol management */ | 512 | /* symbol management */ |
| @@ -1501,6 +1501,8 @@ static void merge_funcattr(struct FuncAttr *fa, struct FuncAttr *fa1) · 1500 unmodified lines | |||
| 1501 | fa->func_ctor = 1; | 1501 | fa->func_ctor = 1; |
| 1502 | if (fa1->func_dtor) | 1502 | if (fa1->func_dtor) |
| 1503 | fa->func_dtor = 1; | 1503 | fa->func_dtor = 1; |
| 1504 | if (fa1->no_bcheck) | ||
| 1505 | fa->no_bcheck = 1; | ||
| 1504 | } | 1506 | } |
| 1505 | 1507 | ||
| 1506 | /* Merge attributes. */ | 1508 | /* Merge attributes. */ |
| @@ -4122,6 +4124,10 @@ redo: · 2615 unmodified lines | |||
| 4122 | case TOK_ALWAYS_INLINE2: | 4124 | case TOK_ALWAYS_INLINE2: |
| 4123 | ad->f.func_alwinl = 1; | 4125 | ad->f.func_alwinl = 1; |
| 4124 | break; | 4126 | break; |
| 4127 | case TOK_NO_BOUND_CHECK1: | ||
| 4128 | case TOK_NO_BOUND_CHECK2: | ||
| 4129 | ad->f.no_bcheck = 1; | ||
| 4130 | break; | ||
| 4125 | case TOK_SECTION1: | 4131 | case TOK_SECTION1: |
| 4126 | case TOK_SECTION2: | 4132 | case TOK_SECTION2: |
| 4127 | skip('('); | 4133 | skip('('); |
| @@ -8079,10 +8085,14 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 3951 unmodified lines | |||
| 8079 | 'cur_text_section' */ | 8085 | 'cur_text_section' */ |
| 8080 | static void gen_function(Sym *sym) | 8086 | static void gen_function(Sym *sym) |
| 8081 | { | 8087 | { |
| 8088 | unsigned char save_bcheck = tcc_state->do_bounds_check; | ||
| 8082 | /* Initialize VLA state */ | 8089 | /* Initialize VLA state */ |
| 8083 | struct scope f = { 0 }; | 8090 | struct scope f = { 0 }; |
| 8084 | cur_scope = root_scope = &f; | 8091 | cur_scope = root_scope = &f; |
| 8085 | 8092 | ||
| 8093 | if (sym->type.ref->f.no_bcheck) | ||
| 8094 | tcc_state->do_bounds_check = 0; | ||
| 8095 | |||
| 8086 | nocode_wanted = 0; | 8096 | nocode_wanted = 0; |
| 8087 | ind = cur_text_section->data_offset; | 8097 | ind = cur_text_section->data_offset; |
| 8088 | if (sym->a.aligned) { | 8098 | if (sym->a.aligned) { |
| @@ -8135,6 +8145,7 @@ static void gen_function(Sym *sym) · 46 unmodified lines | |||
| 8135 | check_vstack(); | 8145 | check_vstack(); |
| 8136 | /* do this after funcend debug info */ | 8146 | /* do this after funcend debug info */ |
| 8137 | next(); | 8147 | next(); |
| 8148 | tcc_state->do_bounds_check = save_bcheck; | ||
| 8138 | } | 8149 | } |
| 8139 | 8150 | ||
| 8140 | static void gen_inline_functions(TCCState *s) | 8151 | static void gen_inline_functions(TCCState *s) |
| @@ -134,6 +134,8 @@ · 133 unmodified lines | |||
| 134 | DEF(TOK_DESTRUCTOR2, "__destructor__") | 134 | DEF(TOK_DESTRUCTOR2, "__destructor__") |
| 135 | DEF(TOK_ALWAYS_INLINE1, "always_inline") | 135 | DEF(TOK_ALWAYS_INLINE1, "always_inline") |
| 136 | DEF(TOK_ALWAYS_INLINE2, "__always_inline__") | 136 | DEF(TOK_ALWAYS_INLINE2, "__always_inline__") |
| 137 | DEF(TOK_NO_BOUND_CHECK1, "bound_no_checking") | ||
| 138 | DEF(TOK_NO_BOUND_CHECK2, "__bound_no_checking__") | ||
| 137 | 139 | ||
| 138 | DEF(TOK_MODE, "__mode__") | 140 | DEF(TOK_MODE, "__mode__") |
| 139 | DEF(TOK_MODE_QI, "__QI__") | 141 | DEF(TOK_MODE_QI, "__QI__") |
| @@ -8,7 +8,7 @@ int foo (struct S *a, struct S *b) · 7 unmodified lines | |||
| 8 | return 0; | 8 | return 0; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | void *memmove(void*,void*,long); | 11 | void *memmove(void*,const void*,__SIZE_TYPE__); |
| 12 | void foo2 (struct S *a, struct S *b) | 12 | void foo2 (struct S *a, struct S *b) |
| 13 | { | 13 | { |
| 14 | memmove(a, b, sizeof *a); | 14 | memmove(a, b, sizeof *a); |
| @@ -8,12 +8,25 @@ · 7 unmodified lines | |||
| 8 | #include <errno.h> | 8 | #include <errno.h> |
| 9 | #include <setjmp.h> | 9 | #include <setjmp.h> |
| 10 | 10 | ||
| 11 | /* See tcc-doc.info */ | ||
| 12 | #if defined(__TINYC__) && __BOUNDS_CHECKING_ON | ||
| 13 | #undef __attribute__ | ||
| 14 | extern void __bound_checking (int no_check); | ||
| 15 | #define BOUNDS_CHECKING_OFF __bound_checking(1) | ||
| 16 | #define BOUNDS_CHECKING_ON __bound_checking(-1) | ||
| 17 | #define BOUNDS_NO_CHECKING __attribute__((bound_no_checking)) | ||
| 18 | #else | ||
| 19 | #define BOUNDS_CHECKING_OFF | ||
| 20 | #define BOUNDS_CHECKING_ON | ||
| 21 | #define BOUNDS_NO_CHECKING | ||
| 22 | #endif | ||
| 23 | |||
| 11 | static volatile int run = 1; | 24 | static volatile int run = 1; |
| 12 | static int dummy[10]; | 25 | static int dummy[10]; |
| 13 | static sem_t sem; | 26 | static sem_t sem; |
| 14 | 27 | ||
| 15 | static void | 28 | static void |
| 16 | add (void) | 29 | add (void) BOUNDS_NO_CHECKING |
| 17 | { | 30 | { |
| 18 | int i; | 31 | int i; |
| 19 | 32 | ||
| @@ -41,29 +54,12 @@ do_signal (void *unused) · 21 unmodified lines | |||
| 41 | return NULL; | 54 | return NULL; |
| 42 | } | 55 | } |
| 43 | 56 | ||
| 44 | /* See tcc-doc.info */ | 57 | static void signal_handler(int sig) BOUNDS_NO_CHECKING |
| 45 | #ifdef __BOUNDS_CHECKING_ON | ||
| 46 | extern void __bound_checking (int no_check); | ||
| 47 | #define BOUNDS_CHECKING_OFF __bound_checking(1) | ||
| 48 | #define BOUNDS_CHECKING_ON __bound_checking(-1) | ||
| 49 | #else | ||
| 50 | #define BOUNDS_CHECKING_OFF | ||
| 51 | #define BOUNDS_CHECKING_ON | ||
| 52 | #endif | ||
| 53 | |||
| 54 | static void real_signal_handler(int sig) | ||
| 55 | { | 58 | { |
| 56 | add(); | 59 | add(); |
| 57 | sem_post (&sem); | 60 | sem_post (&sem); |
| 58 | } | 61 | } |
| 59 | 62 | ||
| 60 | static void signal_handler(int sig) | ||
| 61 | { | ||
| 62 | BOUNDS_CHECKING_OFF; | ||
| 63 | real_signal_handler(sig); | ||
| 64 | BOUNDS_CHECKING_ON; | ||
| 65 | } | ||
| 66 | |||
| 67 | int | 63 | int |
| 68 | main (void) | 64 | main (void) |
| 69 | { | 65 | { |