Add attribute bound_no_checking

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
This commit is contained in:
herman ten brugge 2020-08-13 11:19:11 +02:00
parent 5aaed43efd
commit 50fe33f880
7 changed files with 91 additions and 53 deletions

View File

@ -112,6 +112,12 @@ static sem_t bounds_sem;
#define WAIT_SEM() if (use_sem) while (sem_wait (&bounds_sem) < 0 \ #define WAIT_SEM() if (use_sem) while (sem_wait (&bounds_sem) < 0 \
&& errno == EINTR) && errno == EINTR)
#define POST_SEM() if (use_sem) sem_post (&bounds_sem) #define POST_SEM() if (use_sem) sem_post (&bounds_sem)
#elif 0
static pthread_mutex_t bounds_mtx;
#define INIT_SEM() pthread_mutex_init (&bounds_mtx, NULL)
#define EXIT_SEM() pthread_mutex_destroy (&bounds_mtx)
#define WAIT_SEM() if (use_sem) pthread_mutex_lock (&bounds_mtx)
#define POST_SEM() if (use_sem) pthread_mutex_unlock (&bounds_mtx)
#else #else
static pthread_spinlock_t bounds_spin; static pthread_spinlock_t bounds_spin;
/* about 25% faster then semaphore. */ /* about 25% faster then semaphore. */
@ -618,6 +624,9 @@ void __bound_new_region(void *p, size_t size)
alloca_list_type *cur; alloca_list_type *cur;
alloca_list_type *new; alloca_list_type *new;
if (no_checking)
return;
dprintf(stderr, "%s, %s(): %p, 0x%lx\n", dprintf(stderr, "%s, %s(): %p, 0x%lx\n",
__FILE__, __FUNCTION__, p, (unsigned long)size); __FILE__, __FUNCTION__, p, (unsigned long)size);
GET_CALLER_FP (fp); GET_CALLER_FP (fp);
@ -638,8 +647,7 @@ void __bound_new_region(void *p, size_t size)
last = cur; last = cur;
cur = cur->next; cur = cur->next;
} }
if (no_checking == 0) tree = splay_insert((size_t)p, size, tree);
tree = splay_insert((size_t)p, size, tree);
if (new) { if (new) {
new->fp = fp; new->fp = fp;
new->p = p; new->p = p;
@ -1188,7 +1196,7 @@ void __bound_free(void *ptr, const void *caller)
size_t addr = (size_t) ptr; size_t addr = (size_t) ptr;
void *p; void *p;
if (ptr == NULL || tree == NULL || no_checking if (ptr == NULL || tree == NULL
#if MALLOC_REDIR #if MALLOC_REDIR
|| ((unsigned char *) ptr >= &initial_pool[0] && || ((unsigned char *) ptr >= &initial_pool[0] &&
(unsigned char *) ptr < &initial_pool[sizeof(initial_pool)]) (unsigned char *) ptr < &initial_pool[sizeof(initial_pool)])
@ -1198,25 +1206,27 @@ void __bound_free(void *ptr, const void *caller)
dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr); dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr);
WAIT_SEM (); if (no_checking == 0) {
INCR_COUNT(bound_free_count); WAIT_SEM ();
tree = splay (addr, tree); INCR_COUNT(bound_free_count);
if (tree->start == addr) { tree = splay (addr, tree);
if (tree->is_invalid) { if (tree->start == addr) {
POST_SEM (); if (tree->is_invalid) {
bound_error("freeing invalid region"); POST_SEM ();
return; bound_error("freeing invalid region");
return;
}
tree->is_invalid = 1;
memset (ptr, 0x5a, tree->size);
p = free_reuse_list[free_reuse_index];
free_reuse_list[free_reuse_index] = ptr;
free_reuse_index = (free_reuse_index + 1) % FREE_REUSE_SIZE;
if (p)
tree = splay_delete((size_t)p, tree);
ptr = p;
} }
tree->is_invalid = 1; POST_SEM ();
memset (ptr, 0x5a, tree->size);
p = free_reuse_list[free_reuse_index];
free_reuse_list[free_reuse_index] = ptr;
free_reuse_index = (free_reuse_index + 1) % FREE_REUSE_SIZE;
if (p)
tree = splay_delete((size_t)p, tree);
ptr = p;
} }
POST_SEM ();
BOUND_FREE (ptr); BOUND_FREE (ptr);
} }
@ -1340,8 +1350,7 @@ int __bound_munmap (void *start, size_t size)
/* check that (p ... p + size - 1) lies inside 'p' region, if any */ /* check that (p ... p + size - 1) lies inside 'p' region, if any */
static void __bound_check(const void *p, size_t size, const char *function) static void __bound_check(const void *p, size_t size, const char *function)
{ {
if (no_checking == 0 && size != 0 && if (size != 0 && __bound_ptr_add((void *)p, size) == INVALID_POINTER) {
__bound_ptr_add((void *)p, size) == INVALID_POINTER) {
bound_error("invalid pointer %p, size 0x%lx in %s", bound_error("invalid pointer %p, size 0x%lx in %s",
p, (unsigned long)size, function); p, (unsigned long)size, function);
} }

View File

@ -951,34 +951,53 @@ Here are some examples of caught errors:
Signal handlers are not compatible with bounds checking. The code Signal handlers are not compatible with bounds checking. The code
below can be used to protect signal handlers. below can be used to protect signal handlers.
The call to __bound_checking(1) will disable bounds checking in the The @code{__attribute__((bound_no_checking))} will prevent all bound checking
whole application. code generation. If a signal handler calls another function this
function must also use @code{__attribute__((bound_no_checking))}.
The BOUNDS_CHECKING_OFF and BOUNDS_CHECKING_ON can also be used to 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.
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. disable bounds checking for some code. This is not recommended.
It is better to fix the code. It is better to fix the code.
@example @example
#ifdef __BOUNDS_CHECKING_ON #if defined(__TINYC__) && __BOUNDS_CHECKING_ON
#undef __attribute__
extern void __bound_checking (int no_check); extern void __bound_checking (int no_check);
#define BOUNDS_CHECKING_OFF __bound_checking(1) #define BOUNDS_CHECKING_OFF __bound_checking(1)
#define BOUNDS_CHECKING_ON __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 real_signal_handler(int sig, siginfo_t *info, void *ucontext) void signal_handler(int sig, void *info, void *ucontext) BOUNDS_NO_CHECKING
@{ @{
... ... signal handler code without generated bounds checking code.
@} @}
void signal_handler(int sig, void *info, void *ucontext) void run(const char *cmd)
@{ @{
BOUNDS_CHECKING_OFF; switch (fork()) @{
real_signal_handler(sig, info, data); case 0:
BOUNDS_CHECKING_ON; BOUNDS_CHECKING_OFF;
...
exec...
exit(1);
case -1:
...
break;
default:
...
break;
@}
@} @}
@end example @end example

3
tcc.h
View File

@ -505,7 +505,8 @@ 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 */
xxxx :15; no_bcheck : 1, /* no bound checking */
xxxx :14;
}; };
/* symbol management */ /* symbol management */

View File

@ -1501,6 +1501,8 @@ 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. */
@ -4122,6 +4124,10 @@ redo:
case TOK_ALWAYS_INLINE2: case TOK_ALWAYS_INLINE2:
ad->f.func_alwinl = 1; ad->f.func_alwinl = 1;
break; break;
case TOK_NO_BOUND_CHECK1:
case TOK_NO_BOUND_CHECK2:
ad->f.no_bcheck = 1;
break;
case TOK_SECTION1: case TOK_SECTION1:
case TOK_SECTION2: case TOK_SECTION2:
skip('('); skip('(');
@ -8079,10 +8085,14 @@ 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)
{ {
unsigned char save_bcheck = tcc_state->do_bounds_check;
/* Initialize VLA state */ /* Initialize VLA state */
struct scope f = { 0 }; struct scope f = { 0 };
cur_scope = root_scope = &f; cur_scope = root_scope = &f;
if (sym->type.ref->f.no_bcheck)
tcc_state->do_bounds_check = 0;
nocode_wanted = 0; nocode_wanted = 0;
ind = cur_text_section->data_offset; ind = cur_text_section->data_offset;
if (sym->a.aligned) { if (sym->a.aligned) {
@ -8135,6 +8145,7 @@ static void gen_function(Sym *sym)
check_vstack(); check_vstack();
/* do this after funcend debug info */ /* do this after funcend debug info */
next(); next();
tcc_state->do_bounds_check = save_bcheck;
} }
static void gen_inline_functions(TCCState *s) static void gen_inline_functions(TCCState *s)

View File

@ -134,6 +134,8 @@
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__")
DEF(TOK_NO_BOUND_CHECK1, "bound_no_checking")
DEF(TOK_NO_BOUND_CHECK2, "__bound_no_checking__")
DEF(TOK_MODE, "__mode__") DEF(TOK_MODE, "__mode__")
DEF(TOK_MODE_QI, "__QI__") DEF(TOK_MODE_QI, "__QI__")

View File

@ -8,7 +8,7 @@ int foo (struct S *a, struct S *b)
return 0; return 0;
} }
void *memmove(void*,void*,long); void *memmove(void*,const void*,__SIZE_TYPE__);
void foo2 (struct S *a, struct S *b) void foo2 (struct S *a, struct S *b)
{ {
memmove(a, b, sizeof *a); memmove(a, b, sizeof *a);

View File

@ -8,12 +8,25 @@
#include <errno.h> #include <errno.h>
#include <setjmp.h> #include <setjmp.h>
/* 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))
#else
#define BOUNDS_CHECKING_OFF
#define BOUNDS_CHECKING_ON
#define BOUNDS_NO_CHECKING
#endif
static volatile int run = 1; static volatile int run = 1;
static int dummy[10]; static int dummy[10];
static sem_t sem; static sem_t sem;
static void static void
add (void) add (void) BOUNDS_NO_CHECKING
{ {
int i; int i;
@ -41,29 +54,12 @@ do_signal (void *unused)
return NULL; return NULL;
} }
/* See tcc-doc.info */ static void signal_handler(int sig) BOUNDS_NO_CHECKING
#ifdef __BOUNDS_CHECKING_ON
extern void __bound_checking (int no_check);
#define BOUNDS_CHECKING_OFF __bound_checking(1)
#define BOUNDS_CHECKING_ON __bound_checking(-1)
#else
#define BOUNDS_CHECKING_OFF
#define BOUNDS_CHECKING_ON
#endif
static void real_signal_handler(int sig)
{ {
add(); add();
sem_post (&sem); sem_post (&sem);
} }
static void signal_handler(int sig)
{
BOUNDS_CHECKING_OFF;
real_signal_handler(sig);
BOUNDS_CHECKING_ON;
}
int int
main (void) main (void)
{ {