mirror of
https://github.com/mirror/tinycc.git
synced 2025-01-15 05:20:06 +08:00
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:
parent
5aaed43efd
commit
50fe33f880
53
lib/bcheck.c
53
lib/bcheck.c
@ -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);
|
||||||
}
|
}
|
||||||
|
39
tcc-doc.texi
39
tcc-doc.texi
@ -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
3
tcc.h
@ -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 */
|
||||||
|
11
tccgen.c
11
tccgen.c
@ -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)
|
||||||
|
2
tcctok.h
2
tcctok.h
@ -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__")
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user