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);
/* 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);
}

View File

@ -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

3
tcc.h
View File

@ -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 */

View File

@ -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)

View File

@ -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__")

View File

@ -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