From f9870f786080f788abec9d4b6e565ff02a469a9a Mon Sep 17 00:00:00 2001 From: grischka Date: Fri, 14 Aug 2020 15:00:29 +0200 Subject: [PATCH] bcheck: remove static (compile-time) control Providing both run-time and compile-time control for bounds checking as an user interface appears unnecessary and confusing. Also: - replace 'bound_...' by 'bounds_...' for consistency - tcc-doc: put related info into one place and cleanup The __bounds_checking(x) function is still missing explanation. (I.e. what happens if the accumulated value drops below zero.) --- lib/bcheck.c | 4 +- tcc-doc.texi | 104 +++++++++++++++----------------- tcc.h | 3 +- tccgen.c | 38 ------------ tcctok.h | 4 -- tests/tests2/114_bound_signal.c | 17 +++--- 6 files changed, 59 insertions(+), 111 deletions(-) diff --git a/lib/bcheck.c b/lib/bcheck.c index 9c0a40b6..b5742e35 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -198,7 +198,7 @@ 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 __bounds_checking (int no_check); void __bound_never_fatal (int no_check); DLL_EXPORT void * __bound_ptr_add(void *p, size_t offset); DLL_EXPORT void * __bound_ptr_indir1(void *p, size_t offset); @@ -359,7 +359,7 @@ static void fetch_and_add(int* variable, int value) } /* enable/disable checking. This can be used in signal handlers. */ -void __bound_checking (int no_check) +void __bounds_checking (int no_check) { fetch_and_add (&no_checking, no_check); } diff --git a/tcc-doc.texi b/tcc-doc.texi index 65aff2d1..d8cf070c 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -351,26 +351,8 @@ invalid pointer} instead of the laconic @code{Segmentation fault}. @item -b -Generate additional support code to check -memory allocations and array/pointer bounds. @option{-g} is implied. Note -that the generated code is slower and bigger in this case. -The bound checking code is not included in shared libraries. The main executable should always be compiled with the @option{-b}. - -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 -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 - -Note: @option{-b} is only available on i386 (linux and windows), x86_64 (linux and windows), arm, arm64 and riscv64 for the moment. +Generate additional support code to check memory allocations and array/pointer +bounds (@pxref{Bounds}). @option{-g} is implied. @item -bt[N] Display N callers in stack traces. This is useful with @option{-g} or @option{-b}. @@ -686,8 +668,6 @@ are supported. @item Binary digits can be entered (@code{0b101} instead of @code{5}). -@item @code{__BOUNDS_CHECKING_ON} is defined if bound checking is activated. - @end itemize @node asm @@ -881,16 +861,7 @@ GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a ) @cindex bound checks @cindex memory checks -This feature is activated with the @option{-b} (@pxref{Invoke}). - -Note that pointer size is @emph{unchanged} and that code generated -with bound checks is @emph{fully compatible} with unchecked -code. When a pointer comes from unchecked code, it is assumed to be -valid. Even very obscure C code with casts should work correctly. - -For more information about the ideas behind this method, see -@url{http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html}. - +This feature is activated with the @option{-b} option (@pxref{Invoke}). Here are some examples of caught errors: @table @asis @@ -946,41 +917,58 @@ Here are some examples of caught errors: free(tab); @} @end example - @end table -Signal handlers are not compatible with bounds checking. The code -below can be used to protect signal handlers. -The @code{__attribute__((bound_no_checking))} will prevent all bound checking -code generation. If a signal handler calls another function this -function must also use @code{__attribute__((bound_no_checking))}. +TCC defines @code{__BOUNDS_CHECKING_ON} if activated. -The fork() function call in a multi threaded application is also a problem. -To solve this all bounds checking can be disabled by calling -@code{__bound_checking(1)}. The call to @code{__bound_checking(1)} will disable bounds -checking in the whole application. +There are five environment variables that can be used to control the behavior: +@itemize +@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 +- 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 itemize -The @code{BOUNDS_CHECKING_OFF} and @code{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. +Also, a function @code{__bounds_checking(x)} can be used to turn off/on bounds +checking from usercode (see below). + +Notes: +@itemize +@item Only available on i386 (linux and windows), x86_64 (linux and windows), +arm, arm64 and riscv64 for the moment. +@item The generated code is slower and bigger. +@item The bound checking code is not included in shared libraries. The main +executable should always be compiled with the @option{-b}. +@item Pointer size is @emph{unchanged} and code generated with bound checks is +@emph{fully compatible} with unchecked code. When a pointer comes from +unchecked code, it is assumed to be valid. Even very obscure C code with +casts should work correctly. +@item Signal handlers are not compatible with bounds checking. The fork() +function call in a multi threaded application is also a problem. +The code below can be used to solve this. +@end itemize @example - -#if defined(__TINYC__) && __BOUNDS_CHECKING_ON -#undef __attribute__ -extern void __bound_checking (int no_check); -#define BOUNDS_CHECKING_OFF __bound_checking(1) -#define BOUNDS_CHECKING_ON __bound_checking(-1) -#define BOUNDS_NO_CHECKING __attribute__((bound_no_checking)) +#ifdef __BOUNDS_CHECKING_ON +extern void __bounds_checking (int x); +# define BOUNDS_CHECKING_OFF __bounds_checking(1) +# define BOUNDS_CHECKING_ON __bounds_checking(-1) #else -#define BOUNDS_CHECKING_OFF -#define BOUNDS_CHECKING_ON -#define BOUNDS_NO_CHECKING +# define BOUNDS_CHECKING_OFF +# define BOUNDS_CHECKING_ON #endif -void signal_handler(int sig, void *info, void *ucontext) BOUNDS_NO_CHECKING +void signal_handler(int sig, void *info, void *ucontext) @{ + BOUNDS_CHECKING_OFF; ... signal handler code without generated bounds checking code. + BOUNDS_CHECKING_ON; @} void run(const char *cmd) @@ -999,9 +987,11 @@ void run(const char *cmd) break; @} @} - @end example +For more information about the ideas behind this method, see +@url{http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html}. + @node Libtcc @chapter The @code{libtcc} library diff --git a/tcc.h b/tcc.h index f3783d88..7c152ff7 100644 --- a/tcc.h +++ b/tcc.h @@ -505,8 +505,7 @@ struct FuncAttr { func_dtor : 1, /* attribute((destructor)) */ func_args : 8, /* PE __stdcall args */ func_alwinl : 1, /* always_inline */ - no_bcheck : 1, /* no bound checking */ - xxxx :14; + xxxx : 15; }; /* symbol management */ diff --git a/tccgen.c b/tccgen.c index 72ac7742..03c46ee8 100644 --- a/tccgen.c +++ b/tccgen.c @@ -1501,8 +1501,6 @@ static void merge_funcattr(struct FuncAttr *fa, struct FuncAttr *fa1) fa->func_ctor = 1; if (fa1->func_dtor) fa->func_dtor = 1; - if (fa1->no_bcheck) - fa->no_bcheck = 1; } /* Merge attributes. */ @@ -4129,12 +4127,6 @@ redo: case TOK_ALWAYS_INLINE2: ad->f.func_alwinl = 1; break; -#ifdef CONFIG_TCC_BCHECK - case TOK_NO_BOUND_CHECK1: - case TOK_NO_BOUND_CHECK2: - ad->f.no_bcheck = 1; - break; -#endif case TOK_SECTION1: case TOK_SECTION2: skip('('); @@ -5967,26 +5959,6 @@ special_math_val: if (t < TOK_UIDENT) expect("identifier"); s = sym_find(t); -#ifdef CONFIG_TCC_BCHECK - /* HACK to undo alias definition in tccpp.c - if function has no bound checking */ - if (tcc_state->do_bounds_check == 0 && s && - (s->type.t & VT_BTYPE) == VT_FUNC && (s->asm_label & SYM_FIELD)) { - const char *name = get_tok_str(s->asm_label & ~SYM_FIELD, NULL); - - if (name && strncmp (name, "__bound_", strlen("__bound_")) == 0) { - char str[100]; - int v = s->v; - - sprintf (str, "!%s", name); /* illegal name */ - t = tok_alloc(str, strlen(str))->tok; - s = sym_find(t); - if (s == NULL) - s = external_global_sym(t, &func_old_type); - s->asm_label = v | SYM_FIELD; /* use old name as alias */ - } - } -#endif if (!s || IS_ASM_SYM(s)) { const char *name = get_tok_str(t, NULL); if (tok != '(') @@ -8122,14 +8094,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, 'cur_text_section' */ static void gen_function(Sym *sym) { - /* Initialize VLA state */ struct scope f = { 0 }; -#ifdef CONFIG_TCC_BCHECK - unsigned char save_bcheck = tcc_state->do_bounds_check; - - if (sym->type.ref->f.no_bcheck) - tcc_state->do_bounds_check = 0; -#endif cur_scope = root_scope = &f; nocode_wanted = 0; ind = cur_text_section->data_offset; @@ -8183,9 +8148,6 @@ static void gen_function(Sym *sym) check_vstack(); /* do this after funcend debug info */ next(); -#ifdef CONFIG_TCC_BCHECK - tcc_state->do_bounds_check = save_bcheck; -#endif } static void gen_inline_functions(TCCState *s) diff --git a/tcctok.h b/tcctok.h index f465e8fb..1e9bc4ab 100644 --- a/tcctok.h +++ b/tcctok.h @@ -134,10 +134,6 @@ DEF(TOK_DESTRUCTOR2, "__destructor__") DEF(TOK_ALWAYS_INLINE1, "always_inline") DEF(TOK_ALWAYS_INLINE2, "__always_inline__") -#ifdef CONFIG_TCC_BCHECK - DEF(TOK_NO_BOUND_CHECK1, "bound_no_checking") - DEF(TOK_NO_BOUND_CHECK2, "__bound_no_checking__") -#endif DEF(TOK_MODE, "__mode__") DEF(TOK_MODE_QI, "__QI__") diff --git a/tests/tests2/114_bound_signal.c b/tests/tests2/114_bound_signal.c index d7d7feb8..6af377d2 100644 --- a/tests/tests2/114_bound_signal.c +++ b/tests/tests2/114_bound_signal.c @@ -11,14 +11,12 @@ /* See tcc-doc.info */ #if defined(__TINYC__) && __BOUNDS_CHECKING_ON #undef __attribute__ -extern void __bound_checking (int no_check); -#define BOUNDS_CHECKING_OFF __bound_checking(1) -#define BOUNDS_CHECKING_ON __bound_checking(-1) -#define BOUNDS_NO_CHECKING __attribute__((bound_no_checking)) +extern void __bounds_checking (int no_check); +#define BOUNDS_CHECKING_OFF __bounds_checking(1) +#define BOUNDS_CHECKING_ON __bounds_checking(-1) #else #define BOUNDS_CHECKING_OFF #define BOUNDS_CHECKING_ON -#define BOUNDS_NO_CHECKING #endif static volatile int run = 1; @@ -26,15 +24,16 @@ static int dummy[10]; static sem_t sem; static void -add (void) BOUNDS_NO_CHECKING +add (void) { int i; + BOUNDS_CHECKING_OFF; for (i = 0; i < (sizeof(dummy)/sizeof(dummy[0])); i++) { dummy[i]++; } - /* Should not be translated into __bound_memset */ memset (&dummy[0], 0, sizeof(dummy)); + BOUNDS_CHECKING_ON; } static void * @@ -56,10 +55,12 @@ do_signal (void *unused) return NULL; } -static void signal_handler(int sig) BOUNDS_NO_CHECKING +static void signal_handler(int sig) { + BOUNDS_CHECKING_OFF; add(); sem_post (&sem); + BOUNDS_CHECKING_ON; } int