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.)
This commit is contained in:
grischka 2020-08-14 15:00:29 +02:00
parent a34a9775ba
commit f9870f7860
6 changed files with 59 additions and 111 deletions

View File

@ -198,7 +198,7 @@ static Tree * splay_delete(size_t addr, Tree *t);
void splay_printtree(Tree * t, int d); void splay_printtree(Tree * t, int d);
/* external interface */ /* external interface */
void __bound_checking (int no_check); void __bounds_checking (int no_check);
void __bound_never_fatal (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_add(void *p, size_t offset);
DLL_EXPORT void * __bound_ptr_indir1(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. */ /* 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); fetch_and_add (&no_checking, no_check);
} }

View File

@ -351,26 +351,8 @@ invalid pointer} instead of the laconic @code{Segmentation
fault}. fault}.
@item -b @item -b
Generate additional support code to check Generate additional support code to check memory allocations and array/pointer
memory allocations and array/pointer bounds. @option{-g} is implied. Note bounds (@pxref{Bounds}). @option{-g} is implied.
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.
@item -bt[N] @item -bt[N]
Display N callers in stack traces. This is useful with @option{-g} or @option{-b}. 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 @item Binary digits can be entered (@code{0b101} instead of
@code{5}). @code{5}).
@item @code{__BOUNDS_CHECKING_ON} is defined if bound checking is activated.
@end itemize @end itemize
@node asm @node asm
@ -881,16 +861,7 @@ GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )
@cindex bound checks @cindex bound checks
@cindex memory checks @cindex memory checks
This feature is activated with the @option{-b} (@pxref{Invoke}). This feature is activated with the @option{-b} option (@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}.
Here are some examples of caught errors: Here are some examples of caught errors:
@table @asis @table @asis
@ -946,41 +917,58 @@ Here are some examples of caught errors:
free(tab); free(tab);
@} @}
@end example @end example
@end table @end table
Signal handlers are not compatible with bounds checking. The code TCC defines @code{__BOUNDS_CHECKING_ON} if activated.
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))}.
The fork() function call in a multi threaded application is also a problem. There are five environment variables that can be used to control the behavior:
To solve this all bounds checking can be disabled by calling @itemize
@code{__bound_checking(1)}. The call to @code{__bound_checking(1)} will disable bounds @item TCC_BOUNDS_WARN_POINTER_ADD
checking in the whole application. - 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 Also, a function @code{__bounds_checking(x)} can be used to turn off/on bounds
disable bounds checking for some code. This is not recommended. checking from usercode (see below).
It is better to fix the code.
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 @example
#ifdef __BOUNDS_CHECKING_ON
#if defined(__TINYC__) && __BOUNDS_CHECKING_ON extern void __bounds_checking (int x);
#undef __attribute__ # define BOUNDS_CHECKING_OFF __bounds_checking(1)
extern void __bound_checking (int no_check); # define BOUNDS_CHECKING_ON __bounds_checking(-1)
#define BOUNDS_CHECKING_OFF __bound_checking(1)
#define BOUNDS_CHECKING_ON __bound_checking(-1)
#define BOUNDS_NO_CHECKING __attribute__((bound_no_checking))
#else #else
#define BOUNDS_CHECKING_OFF # define BOUNDS_CHECKING_OFF
#define BOUNDS_CHECKING_ON # define BOUNDS_CHECKING_ON
#define BOUNDS_NO_CHECKING
#endif #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. ... signal handler code without generated bounds checking code.
BOUNDS_CHECKING_ON;
@} @}
void run(const char *cmd) void run(const char *cmd)
@ -999,9 +987,11 @@ void run(const char *cmd)
break; break;
@} @}
@} @}
@end example @end example
For more information about the ideas behind this method, see
@url{http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html}.
@node Libtcc @node Libtcc
@chapter The @code{libtcc} library @chapter The @code{libtcc} library

3
tcc.h
View File

@ -505,8 +505,7 @@ struct FuncAttr {
func_dtor : 1, /* attribute((destructor)) */ func_dtor : 1, /* attribute((destructor)) */
func_args : 8, /* PE __stdcall args */ func_args : 8, /* PE __stdcall args */
func_alwinl : 1, /* always_inline */ func_alwinl : 1, /* always_inline */
no_bcheck : 1, /* no bound checking */ xxxx : 15;
xxxx :14;
}; };
/* symbol management */ /* symbol management */

View File

@ -1501,8 +1501,6 @@ static void merge_funcattr(struct FuncAttr *fa, struct FuncAttr *fa1)
fa->func_ctor = 1; fa->func_ctor = 1;
if (fa1->func_dtor) if (fa1->func_dtor)
fa->func_dtor = 1; fa->func_dtor = 1;
if (fa1->no_bcheck)
fa->no_bcheck = 1;
} }
/* Merge attributes. */ /* Merge attributes. */
@ -4129,12 +4127,6 @@ redo:
case TOK_ALWAYS_INLINE2: case TOK_ALWAYS_INLINE2:
ad->f.func_alwinl = 1; ad->f.func_alwinl = 1;
break; 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_SECTION1:
case TOK_SECTION2: case TOK_SECTION2:
skip('('); skip('(');
@ -5967,26 +5959,6 @@ special_math_val:
if (t < TOK_UIDENT) if (t < TOK_UIDENT)
expect("identifier"); expect("identifier");
s = sym_find(t); 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)) { if (!s || IS_ASM_SYM(s)) {
const char *name = get_tok_str(t, NULL); const char *name = get_tok_str(t, NULL);
if (tok != '(') if (tok != '(')
@ -8122,14 +8094,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
'cur_text_section' */ 'cur_text_section' */
static void gen_function(Sym *sym) static void gen_function(Sym *sym)
{ {
/* Initialize VLA state */
struct scope f = { 0 }; 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; cur_scope = root_scope = &f;
nocode_wanted = 0; nocode_wanted = 0;
ind = cur_text_section->data_offset; ind = cur_text_section->data_offset;
@ -8183,9 +8148,6 @@ static void gen_function(Sym *sym)
check_vstack(); check_vstack();
/* do this after funcend debug info */ /* do this after funcend debug info */
next(); next();
#ifdef CONFIG_TCC_BCHECK
tcc_state->do_bounds_check = save_bcheck;
#endif
} }
static void gen_inline_functions(TCCState *s) static void gen_inline_functions(TCCState *s)

View File

@ -134,10 +134,6 @@
DEF(TOK_DESTRUCTOR2, "__destructor__") DEF(TOK_DESTRUCTOR2, "__destructor__")
DEF(TOK_ALWAYS_INLINE1, "always_inline") DEF(TOK_ALWAYS_INLINE1, "always_inline")
DEF(TOK_ALWAYS_INLINE2, "__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, "__mode__")
DEF(TOK_MODE_QI, "__QI__") DEF(TOK_MODE_QI, "__QI__")

View File

@ -11,14 +11,12 @@
/* See tcc-doc.info */ /* See tcc-doc.info */
#if defined(__TINYC__) && __BOUNDS_CHECKING_ON #if defined(__TINYC__) && __BOUNDS_CHECKING_ON
#undef __attribute__ #undef __attribute__
extern void __bound_checking (int no_check); extern void __bounds_checking (int no_check);
#define BOUNDS_CHECKING_OFF __bound_checking(1) #define BOUNDS_CHECKING_OFF __bounds_checking(1)
#define BOUNDS_CHECKING_ON __bound_checking(-1) #define BOUNDS_CHECKING_ON __bounds_checking(-1)
#define BOUNDS_NO_CHECKING __attribute__((bound_no_checking))
#else #else
#define BOUNDS_CHECKING_OFF #define BOUNDS_CHECKING_OFF
#define BOUNDS_CHECKING_ON #define BOUNDS_CHECKING_ON
#define BOUNDS_NO_CHECKING
#endif #endif
static volatile int run = 1; static volatile int run = 1;
@ -26,15 +24,16 @@ static int dummy[10];
static sem_t sem; static sem_t sem;
static void static void
add (void) BOUNDS_NO_CHECKING add (void)
{ {
int i; int i;
BOUNDS_CHECKING_OFF;
for (i = 0; i < (sizeof(dummy)/sizeof(dummy[0])); i++) { for (i = 0; i < (sizeof(dummy)/sizeof(dummy[0])); i++) {
dummy[i]++; dummy[i]++;
} }
/* Should not be translated into __bound_memset */
memset (&dummy[0], 0, sizeof(dummy)); memset (&dummy[0], 0, sizeof(dummy));
BOUNDS_CHECKING_ON;
} }
static void * static void *
@ -56,10 +55,12 @@ do_signal (void *unused)
return NULL; return NULL;
} }
static void signal_handler(int sig) BOUNDS_NO_CHECKING static void signal_handler(int sig)
{ {
BOUNDS_CHECKING_OFF;
add(); add();
sem_post (&sem); sem_post (&sem);
BOUNDS_CHECKING_ON;
} }
int int