a bounds checking code for the ARCH=x86_64

master
seyko 2015-04-10 15:17:22 +03:00
parent e92dc595cd
commit 559675b90a
8 changed files with 319 additions and 64 deletions

View File

@ -44,7 +44,7 @@ native : TCC = $(TOP)/tcc$(EXESUF)
cross : TCC = $(TOP)/$(TARGET)-tcc$(EXESUF)
I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BCHECK_O)
X86_64_O = libtcc1.o alloca86_64.o
X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o $(BCHECK_O)
ARM_O = libtcc1.o armeabi.o alloca-arm.o
WIN32_O = $(I386_O) crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o
WIN64_O = $(X86_64_O) crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o

View File

@ -0,0 +1,60 @@
/* ---------------------------------------------- */
/* alloca86_64.S */
.globl __bound_alloca
__bound_alloca:
#ifdef TCC_TARGET_PE
# bound checking is not implemented
pop %rdx
mov %rcx,%rax
add $15,%rax
and $-16,%rax
jz p3
p1:
cmp $4096,%rax
jbe p2
test %rax,-4096(%rsp)
sub $4096,%rsp
sub $4096,%rax
jmp p1
p2:
sub %rax,%rsp
mov %rsp,%rax
add $32,%rax
p3:
push %rdx
ret
#else
pop %rdx
mov %rdi,%rax
movl %rax,%rsi # size, a second parm to the __bound_new_region
add $15,%rax
and $-16,%rax
jz p3
sub %rax,%rsp
mov %rsp,%rdi # pointer, a first parm to the __bound_new_region
mov %rsp,%rax
push %rdx
push %rax
call __bound_new_region
pop %rax
pop %rdx
p3:
push %rdx
ret
#endif
/* mark stack as nonexecutable */
#if defined __ELF__ && defined __linux__
.section .note.GNU-stack,"",@progbits
#endif
/* ---------------------------------------------- */

View File

@ -31,6 +31,12 @@
/* #define BOUND_DEBUG */
#ifdef BOUND_DEBUG
#define dprintf(a...) fprintf(a)
#else
#define dprintf(a...)
#endif
/* define so that bound array is static (faster, but use memory if
bound checking not used) */
/* #define BOUND_STATIC */
@ -50,12 +56,12 @@
#define BOUND_T1_BITS 13
#define BOUND_T2_BITS 11
#define BOUND_T3_BITS (32 - BOUND_T1_BITS - BOUND_T2_BITS)
#define BOUND_T3_BITS (sizeof(size_t)*8 - BOUND_T1_BITS - BOUND_T2_BITS)
#define BOUND_E_BITS (sizeof(size_t))
#define BOUND_T1_SIZE (1 << BOUND_T1_BITS)
#define BOUND_T2_SIZE (1 << BOUND_T2_BITS)
#define BOUND_T3_SIZE (1 << BOUND_T3_BITS)
#define BOUND_E_BITS 4
#define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS)
#define BOUND_T23_SIZE (1 << BOUND_T23_BITS)
@ -64,7 +70,7 @@
/* this pointer is generated when bound check is incorrect */
#define INVALID_POINTER ((void *)(-2))
/* size of an empty region */
#define EMPTY_SIZE 0xffffffff
#define EMPTY_SIZE ((size_t)(-1))
/* size of an invalid region */
#define INVALID_SIZE 0
@ -168,9 +174,7 @@ void * FASTCALL __bound_ptr_add(void *p, size_t offset)
__bound_init();
#if defined(BOUND_DEBUG)
printf("%s %s: 0x%x %d\n", __FILE__, __FUNCTION__, (int)p, offset);
#endif
dprintf(stderr, "%s %s: %p %p\n", __FILE__, __FUNCTION__, p, offset);
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
e = (BoundEntry *)((char *)e +
@ -197,6 +201,8 @@ void * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \
size_t addr = (size_t)p; \
BoundEntry *e; \
\
dprintf(stderr, "%s %s: %p %p start\n", __FILE__, __FUNCTION__, p, offset); \
\
__bound_init(); \
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \
e = (BoundEntry *)((char *)e + \
@ -212,6 +218,7 @@ void * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \
fprintf(stderr,"%s %s: %p is outside of the region\n", __FILE__, __FUNCTION__, p + offset); \
return INVALID_POINTER; /* return an invalid pointer */ \
} \
dprintf(stderr, "%s %s: return p+offset = %p\n", __FILE__, __FUNCTION__, p + offset); \
return p + offset; \
}
@ -232,9 +239,8 @@ BOUND_PTR_INDIR(16)
void FASTCALL __bound_local_new(void *p1)
{
size_t addr, size, fp, *p = p1;
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s start p1=%p *p1=%p\n", __FILE__, __FUNCTION__, p, *p);
#endif
dprintf(stderr, "%s, %s start p1=%p\n", __FILE__, __FUNCTION__, p);
GET_CALLER_FP(fp);
for(;;) {
addr = p[0];
@ -245,9 +251,7 @@ void FASTCALL __bound_local_new(void *p1)
p += 2;
__bound_new_region((void *)addr, size);
}
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__);
#endif
dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__);
}
/* called when leaving a function to delete all the local regions */
@ -268,7 +272,7 @@ void FASTCALL __bound_local_delete(void *p1)
static BoundEntry *__bound_new_page(void)
{
BoundEntry *page;
int i;
size_t i;
page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE);
if (!page)
@ -296,11 +300,11 @@ static void bound_free_entry(BoundEntry *e)
libc_free(e);
}
static inline BoundEntry *get_page(int index)
static BoundEntry *get_page(size_t index)
{
BoundEntry *page;
page = __bound_t1[index];
if (page == __bound_empty_t2 || page == __bound_invalid_t2) {
if (!page || page == __bound_empty_t2 || page == __bound_invalid_t2) {
/* create a new page if necessary */
page = __bound_new_page();
__bound_t1[index] = page;
@ -325,7 +329,7 @@ static void mark_invalid(size_t addr, size_t size)
t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS);
#if 0
printf("mark_invalid: start = %x %x\n", t2_start, t2_end);
dprintf(stderr, "mark_invalid: start = %x %x\n", t2_start, t2_end);
#endif
/* first we handle full pages */
@ -364,7 +368,7 @@ static void mark_invalid(size_t addr, size_t size)
void __bound_init(void)
{
int i;
size_t i;
BoundEntry *page;
size_t start, size;
size_t *p;
@ -375,9 +379,7 @@ void __bound_init(void)
inited = 1;
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__);
#endif
dprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__);
/* save malloc hooks and install bound check hooks */
install_malloc_hooks();
@ -445,19 +447,18 @@ void __bound_init(void)
__bound_new_region((void *)p[0], p[1]);
p += 2;
}
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__);
#endif
dprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__);
}
void __bound_main_arg(void **p)
{
void *start = p;
while (*p++);
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s calling __bound_new_region(%p, %p)\n",
dprintf(stderr, "%s, %s calling __bound_new_region(%p, %p)\n",
__FILE__, __FUNCTION__, (void *) p - start);
#endif
__bound_new_region(start, (void *) p - start);
}
@ -495,10 +496,8 @@ void __bound_new_region(void *p, size_t size)
__bound_init();
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s(%p, %p) start\n",
dprintf(stderr, "%s, %s(%p, %p) start\n",
__FILE__, __FUNCTION__, p, size);
#endif
start = (size_t)p;
end = start + size;
@ -553,9 +552,8 @@ void __bound_new_region(void *p, size_t size)
}
add_region(e, start, size);
}
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__);
#endif
dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__);
}
/* delete a region */
@ -610,9 +608,7 @@ int __bound_delete_region(void *p)
__bound_init();
#ifdef BOUND_DEBUG
fprintf(stderr, "%s %s() start\n", __FILE__, __FUNCTION__);
#endif
dprintf(stderr, "%s %s() start\n", __FILE__, __FUNCTION__);
start = (size_t)p;
t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
@ -681,9 +677,7 @@ int __bound_delete_region(void *p)
delete_region(e, p, empty_size);
}
#ifdef BOUND_DEBUG
fprintf(stderr, "%s %s() end\n", __FILE__, __FUNCTION__);
#endif
dprintf(stderr, "%s %s() end\n", __FILE__, __FUNCTION__);
return 0;
}
@ -771,10 +765,9 @@ void *__bound_malloc(size_t size, const void *caller)
if (!ptr)
return NULL;
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s calling __bound_new_region(%p, %p)\n",
dprintf(stderr, "%s, %s calling __bound_new_region(%p, %p)\n",
__FILE__, __FUNCTION__, ptr, size);
#endif
__bound_new_region(ptr, size);
return ptr;
}
@ -805,10 +798,9 @@ void *__bound_memalign(size_t size, size_t align, const void *caller)
if (!ptr)
return NULL;
#ifdef BOUND_DEBUG
fprintf(stderr, "%s, %s calling __bound_new_region(%p, %p)\n",
dprintf(stderr, "%s, %s calling __bound_new_region(%p, %p)\n",
__FILE__, __FUNCTION__, ptr, size);
#endif
__bound_new_region(ptr, size);
return ptr;
}
@ -861,23 +853,23 @@ void *__bound_calloc(size_t nmemb, size_t size)
static void bound_dump(void)
{
BoundEntry *page, *e;
int i, j;
size_t i, j;
printf("region dump:\n");
fprintf(stderr, "region dump:\n");
for(i=0;i<BOUND_T1_SIZE;i++) {
page = __bound_t1[i];
for(j=0;j<BOUND_T2_SIZE;j++) {
e = page + j;
/* do not print invalid or empty entries */
if (e->size != EMPTY_SIZE && e->start != 0) {
printf("%08x:",
fprintf(stderr, "%08x:",
(i << (BOUND_T2_BITS + BOUND_T3_BITS)) +
(j << BOUND_T3_BITS));
do {
printf(" %08lx:%08lx", e->start, e->start + e->size);
fprintf(stderr, " %08lx:%08lx", e->start, e->start + e->size);
e = e->next;
} while (e != NULL);
printf("\n");
fprintf(stderr, "\n");
}
}
}
@ -891,19 +883,27 @@ static void __bound_check(const void *p, size_t size)
{
if (size == 0)
return;
p = __bound_ptr_add((void *)p, size);
p = __bound_ptr_add((void *)p, size - 1);
if (p == INVALID_POINTER)
bound_error("invalid pointer");
}
void *__bound_memcpy(void *dst, const void *src, size_t size)
{
void* p;
dprintf(stderr, "%s %s: start, dst=%p src=%p size=%p\n", __FILE__, __FUNCTION__, dst, src, size);
__bound_check(dst, size);
__bound_check(src, size);
/* check also region overlap */
if (src >= dst && src < dst + size)
bound_error("overlapping regions in memcpy()");
return memcpy(dst, src, size);
p = memcpy(dst, src, size);
dprintf(stderr, "%s %s: end, p=%p\n", __FILE__, __FUNCTION__, p);
return p;
}
void *__bound_memmove(void *dst, const void *src, size_t size)
@ -939,7 +939,12 @@ int __bound_strlen(const char *s)
char *__bound_strcpy(char *dst, const char *src)
{
int len;
size_t len;
void *p;
dprintf(stderr, "%s %s: strcpy start, dst=%p src=%p\n", __FILE__, __FUNCTION__, dst, src);
len = __bound_strlen(src);
return __bound_memcpy(dst, src, len + 1);
p = __bound_memcpy(dst, src, len + 1);
dprintf(stderr, "%s %s: strcpy end, p=%p\n", __FILE__, __FUNCTION__, dst, src, p);
return p;
}

2
tcc.h
View File

@ -164,7 +164,7 @@
#if !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \
!defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \
!defined(TCC_TARGET_X86_64) && !defined(CONFIG_USE_LIBGCC)
!defined(CONFIG_USE_LIBGCC)
#define CONFIG_TCC_BCHECK /* enable bound checking code */
#endif

View File

@ -1579,7 +1579,6 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1)
add_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
bounds_section->sh_num, "__bounds_start");
#ifdef TCC_TARGET_I386
if (s1->output_type != TCC_OUTPUT_MEMORY) {
/* add 'call __bound_init()' in .init section */
@ -1601,7 +1600,6 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1)
tcc_warning("__bound_init not defined");
}
#endif
#endif
}
/* add tcc runtime libraries */

View File

@ -708,7 +708,7 @@ static void gbound(void)
lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
/* must save type because we must set it to int to get pointer */
type1 = vtop->type;
vtop->type.t = VT_INT;
vtop->type.t = VT_PTR;
gaddrof();
vpushi(0);
gen_bounded_ptr_add();
@ -1752,7 +1752,22 @@ ST_FUNC void gen_op(int op)
#endif
}
gen_op('*');
#ifdef CONFIG_TCC_BCHECK
#if 0
/* #ifdef CONFIG_TCC_BCHECK
The main reason to removing this code:
#include <stdio.h>
int main ()
{
int v[10];
int i = 10;
int j = 9;
fprintf(stderr, "v+i-j = %p\n", v+i-j);
fprintf(stderr, "v+(i-j) = %p\n", v+(i-j));
}
When this code is on. then the output looks like
v+i-j = 0xfffffffe
v+(i-j) = 0xbff84000
*/
/* if evaluating constant expression, no code should be
generated, so no bound check */
if (tcc_state->do_bounds_check && !const_wanted) {

View File

@ -50,12 +50,15 @@ int test4(void)
int i, sum = 0;
int *tab4;
fprintf(stderr, "%s start\n", __FUNCTION__);
tab4 = malloc(20 * sizeof(int));
for(i=0;i<20;i++) {
sum += tab4[i];
}
free(tab4);
fprintf(stderr, "%s end\n", __FUNCTION__);
return sum;
}
@ -65,12 +68,15 @@ int test5(void)
int i, sum = 0;
int *tab4;
fprintf(stderr, "%s start\n", __FUNCTION__);
tab4 = malloc(20 * sizeof(int));
for(i=0;i<21;i++) {
sum += tab4[i];
}
free(tab4);
fprintf(stderr, "%s end\n", __FUNCTION__);
return sum;
}
@ -187,8 +193,43 @@ int test15(void)
return strlen(p);
}
/* ok */
int test16()
{
char *demo = "This is only a test.";
char *p;
fprintf(stderr, "%s start\n", __FUNCTION__);
p = alloca(16);
strcpy(p,"12345678901234");
printf("alloca: p is %s\n", p);
/* Test alloca embedded in a larger expression */
printf("alloca: %s\n", strcpy(alloca(strlen(demo)+1),demo) );
fprintf(stderr, "%s end\n", __FUNCTION__);
}
/* error */
int test17()
{
char *demo = "This is only a test.";
char *p;
fprintf(stderr, "%s start\n", __FUNCTION__);
p = alloca(16);
strcpy(p,"12345678901234");
printf("alloca: p is %s\n", p);
/* Test alloca embedded in a larger expression */
printf("alloca: %s\n", strcpy(alloca(strlen(demo)),demo) );
fprintf(stderr, "%s end\n", __FUNCTION__);
}
int (*table_test[])(void) = {
test1,
test1,
test2,
test3,
@ -204,23 +245,33 @@ int (*table_test[])(void) = {
test13,
test14,
test15,
test16,
test17,
};
int main(int argc, char **argv)
{
int index;
int (*ftest)(void);
int index_max = sizeof(table_test)/sizeof(table_test[0]);
if (argc < 2) {
printf("usage: boundtest n\n"
"test TCC bound checking system\n"
);
printf(
"test TCC bound checking system\n"
"usage: boundtest N\n"
" 1 <= N <= %d\n", index_max);
exit(1);
}
index = 0;
if (argc >= 2)
index = atoi(argv[1]);
index = atoi(argv[1]) - 1;
if ((index < 0) || (index >= index_max)) {
printf("N is outside of the valid range (%d)\n", index);
exit(2);
}
/* well, we also use bounds on this ! */
ftest = table_test[index];
ftest();

View File

@ -625,6 +625,90 @@ static void gcall_or_jmp(int is_jmp)
}
}
#if defined(CONFIG_TCC_BCHECK)
#ifndef TCC_TARGET_PE
static addr_t func_bound_offset;
static unsigned long func_bound_ind;
#endif
static void gen_static_call(int v)
{
Sym *sym = external_global_sym(v, &func_old_type, 0);
oad(0xe8, -4);
greloc(cur_text_section, sym, ind-4, R_X86_64_PC32);
}
/* generate a bounded pointer addition */
ST_FUNC void gen_bounded_ptr_add(void)
{
/* save all temporary registers */
save_regs(0);
/* prepare fast x86_64 function call */
gv(RC_RAX);
o(0xc68948); // mov %rax,%rsi ## second arg in %rsi, this must be size
vtop--;
gv(RC_RAX);
o(0xc78948); // mov %rax,%rdi ## first arg in %rdi, this must be ptr
vtop--;
/* do a fast function call */
gen_static_call(TOK___bound_ptr_add);
/* returned pointer is in rax */
vtop++;
vtop->r = TREG_RAX | VT_BOUNDED;
/* relocation offset of the bounding function call point */
vtop->c.ull = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela)));
}
/* patch pointer addition in vtop so that pointer dereferencing is
also tested */
ST_FUNC void gen_bounded_ptr_deref(void)
{
addr_t func;
int size, align;
ElfW(Rela) *rel;
Sym *sym;
size = 0;
/* XXX: put that code in generic part of tcc */
if (!is_float(vtop->type.t)) {
if (vtop->r & VT_LVAL_BYTE)
size = 1;
else if (vtop->r & VT_LVAL_SHORT)
size = 2;
}
if (!size)
size = type_size(&vtop->type, &align);
switch(size) {
case 1: func = TOK___bound_ptr_indir1; break;
case 2: func = TOK___bound_ptr_indir2; break;
case 4: func = TOK___bound_ptr_indir4; break;
case 8: func = TOK___bound_ptr_indir8; break;
case 12: func = TOK___bound_ptr_indir12; break;
case 16: func = TOK___bound_ptr_indir16; break;
default:
tcc_error("unhandled size when dereferencing bounded pointer");
func = 0;
break;
}
sym = external_global_sym(func, &func_old_type, 0);
if (!sym->c)
put_extern_sym(sym, NULL, 0, 0);
/* patch relocation */
/* XXX: find a better solution ? */
rel = (ElfW(Rela) *)(cur_text_section->reloc->data + vtop->c.ull);
rel->r_info = ELF64_R_INFO(sym->c, ELF64_R_TYPE(rel->r_info));
}
#endif
#ifdef TCC_TARGET_PE
#define REGN 4
@ -1520,6 +1604,17 @@ void gfunc_prolog(CType *func_type)
sym_push(sym->v & ~SYM_FIELD, type,
VT_LOCAL | VT_LVAL, param_addr);
}
#ifdef CONFIG_TCC_BCHECK
/* leave some room for bound checking code */
if (tcc_state->do_bounds_check) {
func_bound_offset = lbounds_section->data_offset;
func_bound_ind = ind;
oad(0xb8, 0); /* lbound section pointer */
o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */
oad(0xb8, 0); /* call to function */
}
#endif
}
/* generate function epilog */
@ -1527,6 +1622,37 @@ void gfunc_epilog(void)
{
int v, saved_ind;
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check
&& func_bound_offset != lbounds_section->data_offset)
{
addr_t saved_ind;
addr_t *bounds_ptr;
Sym *sym_data;
/* add end of table info */
bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
*bounds_ptr = 0;
/* generate bound local allocation */
sym_data = get_sym_ref(&char_pointer_type, lbounds_section,
func_bound_offset, lbounds_section->data_offset);
saved_ind = ind;
ind = func_bound_ind;
greloc(cur_text_section, sym_data, ind + 1, R_386_32);
ind = ind + 5 + 3;
gen_static_call(TOK___bound_local_new);
ind = saved_ind;
/* generate bound check local freeing */
o(0x5250); /* save returned value, if any */
greloc(cur_text_section, sym_data, ind + 1, R_386_32);
oad(0xb8, 0); /* mov xxx, %rax */
o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */
gen_static_call(TOK___bound_local_delete);
o(0x585a); /* restore returned value, if any */
}
#endif
o(0xc9); /* leave */
if (func_ret_sub == 0) {
o(0xc3); /* ret */