From 559675b90acec03b67ed3f2cece1940f00e44c49 Mon Sep 17 00:00:00 2001 From: seyko Date: Fri, 10 Apr 2015 15:17:22 +0300 Subject: [PATCH] a bounds checking code for the ARCH=x86_64 --- lib/Makefile | 2 +- lib/alloca86_64-bt.S | 60 ++++++++++++++++++++++++ lib/bcheck.c | 111 +++++++++++++++++++++++---------------------- tcc.h | 2 +- tccelf.c | 2 - tccgen.c | 19 +++++++- tests/boundtest.c | 61 +++++++++++++++++++++++-- x86_64-gen.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 319 insertions(+), 64 deletions(-) create mode 100644 lib/alloca86_64-bt.S diff --git a/lib/Makefile b/lib/Makefile index aa3984b2..348cb721 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/alloca86_64-bt.S b/lib/alloca86_64-bt.S new file mode 100644 index 00000000..ab8629da --- /dev/null +++ b/lib/alloca86_64-bt.S @@ -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 +/* ---------------------------------------------- */ diff --git a/lib/bcheck.c b/lib/bcheck.c index 7dcb36e1..829e33d6 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -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;isize != 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; } diff --git a/tcc.h b/tcc.h index d9c24dcb..e25a55b5 100644 --- a/tcc.h +++ b/tcc.h @@ -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 diff --git a/tccelf.c b/tccelf.c index ac914562..98815e53 100644 --- a/tccelf.c +++ b/tccelf.c @@ -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 */ diff --git a/tccgen.c b/tccgen.c index 3b87d0a4..9fab0c8c 100644 --- a/tccgen.c +++ b/tccgen.c @@ -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 + 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) { diff --git a/tests/boundtest.c b/tests/boundtest.c index eb95a963..15bffb4e 100644 --- a/tests/boundtest.c +++ b/tests/boundtest.c @@ -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,9 +193,44 @@ 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, test4, @@ -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(); diff --git a/x86_64-gen.c b/x86_64-gen.c index d5ed4f67..0083f8b0 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -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 */ -- 2.11.4.GIT