Update bounds checking.

The bounds checking code has now enabled gen_bounded_ptr_add tests.
This makes the code slower but finds more errors.
I had to correct some things in tcc to make it work.
- Fixed off by one in lib/bcheck.c
- Corrected tccelf.c sym_versions.
- Disabled USE_TAL when using bounds checking.
- Fixed cstr_printf va_start.
- Fixed tests/tests2/46_grep.c off by one error.
- Updated gen_bounded_ptr_add in x86_64-gen.c
- Fixed x86_64-link.c pointer diff.
For gen_vla_alloc now always use alloca call when bounds checking.
Added line/filename in %rax before bound calls to find location of error.
This commit is contained in:
herman ten brugge 2019-12-12 12:56:06 +01:00
parent ce4eafb34f
commit 4a2e33d160
8 changed files with 166 additions and 58 deletions

View file

@ -1130,6 +1130,24 @@ ST_FUNC void gen_bounded_ptr_add(void)
{ {
/* save all temporary registers */ /* save all temporary registers */
save_regs(0); save_regs(0);
/* trick to get line/file_name in code */
o(0xb8);
gen_le32 (file->line_num);
{
int i;
int len;
char *cp = file->filename;
while (*cp) cp++;
while (cp != file->filename && cp[-1] != '/') cp--;
len = strlen (cp);
while (len > 0) {
memcpy (&i, cp, 4);
o(0xb8);
gen_le32 (i);
cp += 4;
len -= 4;
}
}
/* prepare fast i386 function call (args in eax and edx) */ /* prepare fast i386 function call (args in eax and edx) */
gv2(RC_EAX, RC_EDX); gv2(RC_EAX, RC_EDX);
vtop -= 2; vtop -= 2;
@ -1199,22 +1217,31 @@ ST_FUNC void gen_vla_sp_restore(int addr) {
/* Subtract from the stack pointer, and push the resulting value onto the stack */ /* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) { ST_FUNC void gen_vla_alloc(CType *type, int align) {
#ifdef TCC_TARGET_PE int use_call = 0;
/* alloca does more than just adjust %rsp on Windows */
vpush_global_sym(&func_old_type, TOK_alloca); #if defined(CONFIG_TCC_BCHECK)
vswap(); /* Move alloca ref past allocation size */ use_call = tcc_state->do_bounds_check;
gfunc_call(1);
#else
int r;
r = gv(RC_INT); /* allocation size */
/* sub r,%rsp */
o(0x2b);
o(0xe0 | r);
/* We align to 16 bytes rather than align */
/* and ~15, %esp */
o(0xf0e483);
vpop();
#endif #endif
#ifdef TCC_TARGET_PE /* alloca does more than just adjust %rsp on Windows */
use_call = 1;
#endif
if (use_call)
{
vpush_global_sym(&func_old_type, TOK_alloca);
vswap(); /* Move alloca ref past allocation size */
gfunc_call(1);
}
else {
int r;
r = gv(RC_INT); /* allocation size */
/* sub r,%rsp */
o(0x2b);
o(0xe0 | r);
/* We align to 16 bytes rather than align */
/* and ~15, %esp */
o(0xf0e483);
vpop();
}
} }
/* end of X86 code generator */ /* end of X86 code generator */

View file

@ -196,7 +196,7 @@ void * FASTCALL __bound_ptr_add(void *p, size_t offset)
} }
if (addr <= tree->size) { if (addr <= tree->size) {
addr += offset; addr += offset;
if (tree->is_invalid || addr >= tree->size) { if (tree->is_invalid || addr > tree->size) {
fprintf(stderr,"%s %s: %p is outside of the region\n", fprintf(stderr,"%s %s: %p is outside of the region\n",
__FILE__, __FUNCTION__, p + offset); __FILE__, __FUNCTION__, p + offset);
if (never_fatal == 0) { if (never_fatal == 0) {
@ -804,7 +804,7 @@ static void __bound_check(const void *p, size_t size)
return; return;
if (size == 0) if (size == 0)
return; return;
p = __bound_ptr_add((void *)p, size - 1); p = __bound_ptr_add((void *)p, size);
if (p == INVALID_POINTER) if (p == INVALID_POINTER)
bound_error("invalid pointer"); bound_error("invalid pointer");
} }

View file

@ -591,7 +591,7 @@ version_add (TCCState *s1)
prev = sv->prev_same_lib; prev = sv->prev_same_lib;
if (sv->out_index > 0) { if (sv->out_index > 0) {
vna = section_ptr_add(verneed_section, sizeof(*vna)); vna = section_ptr_add(verneed_section, sizeof(*vna));
vna->vna_hash = elf_hash (sv->version); vna->vna_hash = elf_hash ((const unsigned char *)sv->version);
vna->vna_flags = 0; vna->vna_flags = 0;
vna->vna_other = sv->out_index; vna->vna_other = sv->out_index;
sv->out_index = -2; sv->out_index = -2;
@ -599,7 +599,8 @@ version_add (TCCState *s1)
vna->vna_next = sizeof (*vna); vna->vna_next = sizeof (*vna);
n_same_libs++; n_same_libs++;
} }
sv = &sym_versions[prev]; if (prev >= 0)
sv = &sym_versions[prev];
} while(prev >= 0); } while(prev >= 0);
vna->vna_next = 0; vna->vna_next = 0;
vn = (ElfW(Verneed)*)(verneed_section->data + vnofs); vn = (ElfW(Verneed)*)(verneed_section->data + vnofs);

View file

@ -2491,9 +2491,8 @@ redo:
#endif #endif
} }
gen_op('*'); gen_op('*');
#if 0 #ifdef CONFIG_TCC_BCHECK
/* #ifdef CONFIG_TCC_BCHECK /* The main reason to removing this code:
The main reason to removing this code:
#include <stdio.h> #include <stdio.h>
int main () int main ()
{ {
@ -2506,10 +2505,11 @@ redo:
When this code is on. then the output looks like When this code is on. then the output looks like
v+i-j = 0xfffffffe v+i-j = 0xfffffffe
v+(i-j) = 0xbff84000 v+(i-j) = 0xbff84000
This should now work in updated bcheck.c version.
*/ */
/* if evaluating constant expression, no code should be /* if evaluating constant expression, no code should be
generated, so no bound check */ generated, so no bound check */
if (tcc_state->do_bounds_check && !const_wanted) { if (tcc_state->do_bounds_check && !const_wanted && !nocode_wanted) {
/* if bounded pointers, we generate a special code to /* if bounded pointers, we generate a special code to
test bounds */ test bounds */
if (op == '-') { if (op == '-') {

View file

@ -111,7 +111,9 @@ ST_FUNC void expect(const char *msg)
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
/* Custom allocator for tiny objects */ /* Custom allocator for tiny objects */
#ifndef __BOUNDS_CHECKING_ON
#define USE_TAL #define USE_TAL
#endif
#ifndef USE_TAL #ifndef USE_TAL
#define tal_free(al, p) tcc_free(p) #define tal_free(al, p) tcc_free(p)
@ -390,15 +392,16 @@ ST_FUNC void cstr_reset(CString *cstr)
ST_FUNC int cstr_printf(CString *cstr, const char *fmt, ...) ST_FUNC int cstr_printf(CString *cstr, const char *fmt, ...)
{ {
va_list v; va_list v;
va_list vc;
int len, size; int len, size;
va_start(v, fmt); va_start(v, fmt);
len = vsnprintf(NULL, 0, fmt, v); va_copy (vc, v);
va_end(v); len = vsnprintf(NULL, 0, fmt, vc);
va_end(vc);
size = cstr->size + len + 1; size = cstr->size + len + 1;
if (size > cstr->size_allocated) if (size > cstr->size_allocated)
cstr_realloc(cstr, size); cstr_realloc(cstr, size);
va_start(v, fmt);
vsnprintf((char*)cstr->data + cstr->size, size, fmt, v); vsnprintf((char*)cstr->data + cstr->size, size, fmt, v);
va_end(v); va_end(v);
cstr->size += len; cstr->size += len;

View file

@ -456,11 +456,13 @@ char *pmatch(char *line, char *pattern)
while (*l && (e = pmatch(l, p))) while (*l && (e = pmatch(l, p)))
l = e; /* Get longest match */ l = e; /* Get longest match */
while (*p++ != ENDPAT); /* Skip over pattern */ while (*p++ != ENDPAT); /* Skip over pattern */
while (l >= are) { /* Try to match rest */ while (l > are) { /* Try to match rest */
if (e = pmatch(l, p)) if (e = pmatch(l, p))
return(e); return(e);
--l; /* Nope, try earlier */ --l; /* Nope, try earlier */
} }
if (e = pmatch(l, p))
return(e);
return(0); /* Nothing else worked */ return(0); /* Nothing else worked */
default: default:

View file

@ -142,7 +142,9 @@ ST_DATA const int reg_classes[NB_REGS] = {
static unsigned long func_sub_sp_offset; static unsigned long func_sub_sp_offset;
static int func_ret_sub; static int func_ret_sub;
static int in_call; static int nested_call;
static int call_used_nr_reg;
static int call_used_regs[20];
/* XXX: make it faster ? */ /* XXX: make it faster ? */
ST_FUNC void g(int c) ST_FUNC void g(int c)
@ -738,15 +740,55 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1, Section *bound_sec, Section *sym_sec)
/* generate a bounded pointer addition */ /* generate a bounded pointer addition */
ST_FUNC void gen_bounded_ptr_add(void) ST_FUNC void gen_bounded_ptr_add(void)
{ {
int i;
nested_call++;
/* save all temporary registers */ /* save all temporary registers */
save_regs(0); save_regs(0);
if (in_call) { for (i = 0; i < call_used_nr_reg; i++) {
o(0x51525657); /* push $rdi/%rsi/%rdx/%rcx */ switch (call_used_regs[i]) {
o(0x51415041); /* push $r8/%r9 */ case TREG_RAX: case TREG_RCX: case TREG_RDX: case TREG_RSP:
o(0x53415241); /* push $r10/%r11 */ case TREG_RSI: case TREG_RDI:
o(0x50 + call_used_regs[i]); /* push reg */
break;
case TREG_R8: case TREG_R9: case TREG_R10: case TREG_R11:
o(0x5041 + (call_used_regs[i] - TREG_R8) * 0x100); /* push reg */
break;
case TREG_XMM0: case TREG_XMM1: case TREG_XMM2: case TREG_XMM3:
case TREG_XMM4: case TREG_XMM5: case TREG_XMM6: case TREG_XMM7:
o(0x10ec8348); /* sub $10,%rsp */
/* vmovdqu %xmmx,(%rsp) */
o(0x047ffac5 + (call_used_regs[i] - TREG_XMM0) * 0x8000000); o(0x24);
break;
}
} }
if (nested_call > 1) {
#ifdef TCC_TARGET_PE
o(0x5152); /* push %rcx/%rdx */
#else
o(0x5657); /* push %rdi/%rsi */
#endif
}
/* trick to get line/file_name in code */
o(0xb8);
gen_le32 (file->line_num);
{
int len;
char *cp = file->filename;
while (*cp) cp++;
while (cp != file->filename && cp[-1] != '/') cp--;
len = strlen (cp);
while (len > 0) {
memcpy (&i, cp, 4);
o(0xb8);
gen_le32 (i);
cp += 4;
len -= 4;
}
}
/* prepare fast x86_64 function call */ /* prepare fast x86_64 function call */
gv(RC_RAX); gv(RC_RAX);
#ifdef TCC_TARGET_PE #ifdef TCC_TARGET_PE
@ -775,18 +817,38 @@ ST_FUNC void gen_bounded_ptr_add(void)
o(0x20c48348); /* add $20, %rsp */ o(0x20c48348); /* add $20, %rsp */
#endif #endif
if (nested_call > 1) {
#ifdef TCC_TARGET_PE
o(0x5a59); /* pop %rcx/%rdx */
#else
o(0x5f5e); /* pop %rsi/%rdi */
#endif
}
/* returned pointer is in rax */ /* returned pointer is in rax */
vtop++; vtop++;
vtop->r = TREG_RAX | VT_BOUNDED; vtop->r = TREG_RAX | VT_BOUNDED;
if (in_call) { for (i = call_used_nr_reg - 1; i >= 0; i--) {
o(0x5a415b41); /* pop $r11/%r10 */ switch (call_used_regs[i]) {
o(0x58415941); /* pop $r9/%r8 */ case TREG_RAX: case TREG_RCX: case TREG_RDX: case TREG_RSP:
o(0x5f5e5a59); /* pop $rcx/$rdx/$rsi/%rdi */ case TREG_RSI: case TREG_RDI:
o(0x58 + call_used_regs[i]); /* pop reg */
break;
case TREG_R8: case TREG_R9: case TREG_R10: case TREG_R11:
o(0x5841 + (call_used_regs[i] - TREG_R8) * 0x100); /* pop reg */
break;
case TREG_XMM0: case TREG_XMM1: case TREG_XMM2: case TREG_XMM3:
case TREG_XMM4: case TREG_XMM5: case TREG_XMM6: case TREG_XMM7:
/* vmovdqu (%rsp),%xmmx */
o(0x046ffac5 + (call_used_regs[i] - TREG_XMM0) * 0x8000000); o(0x24);
o(0x10c48348); /* add $10,%rsp */
break;
}
} }
/* relocation offset of the bounding function call point */ /* relocation offset of the bounding function call point */
vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela))); vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela)));
nested_call--;
} }
/* patch pointer addition in vtop so that pointer dereferencing is /* patch pointer addition in vtop so that pointer dereferencing is
@ -913,8 +975,6 @@ void gfunc_call(int nb_args)
int size, r, args_size, i, d, bt, struct_size; int size, r, args_size, i, d, bt, struct_size;
int arg; int arg;
in_call = 1;
args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE; args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE;
arg = nb_args; arg = nb_args;
@ -974,6 +1034,7 @@ void gfunc_call(int nb_args)
} else { } else {
d = arg_prepare_reg(arg); d = arg_prepare_reg(arg);
gen_offs_sp(0x8d, d, struct_size); gen_offs_sp(0x8d, d, struct_size);
call_used_regs[call_used_nr_reg++] = d;
} }
struct_size += size; struct_size += size;
} else { } else {
@ -992,6 +1053,7 @@ void gfunc_call(int nb_args)
o(0x66); o(0x66);
orex(1,d,0, 0x7e0f); orex(1,d,0, 0x7e0f);
o(0xc0 + arg*8 + REG_VALUE(d)); o(0xc0 + arg*8 + REG_VALUE(d));
call_used_regs[call_used_nr_reg++] = d;
} }
} else { } else {
if (bt == VT_STRUCT) { if (bt == VT_STRUCT) {
@ -1007,6 +1069,7 @@ void gfunc_call(int nb_args)
d = arg_prepare_reg(arg); d = arg_prepare_reg(arg);
orex(1,d,r,0x89); /* mov */ orex(1,d,r,0x89); /* mov */
o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
call_used_regs[call_used_nr_reg++] = d;
} }
} }
} }
@ -1045,7 +1108,7 @@ void gfunc_call(int nb_args)
o(0xc089); /* mov %eax,%eax */ o(0xc089); /* mov %eax,%eax */
#endif #endif
vtop--; vtop--;
in_call = 0; call_used_nr_reg = 0;
} }
@ -1399,8 +1462,6 @@ void gfunc_call(int nb_args)
int sse_reg, gen_reg; int sse_reg, gen_reg;
char _onstack[nb_args ? nb_args : 1], *onstack = _onstack; char _onstack[nb_args ? nb_args : 1], *onstack = _onstack;
in_call = 1;
/* calculate the number of integer/float register arguments, remember /* calculate the number of integer/float register arguments, remember
arguments to be passed via stack (in onstack[]), and also remember arguments to be passed via stack (in onstack[]), and also remember
if we have to align the stack pointer to 16 (onstack[i] == 2). Needs if we have to align the stack pointer to 16 (onstack[i] == 2). Needs
@ -1533,11 +1594,14 @@ void gfunc_call(int nb_args)
o(0x280f); o(0x280f);
o(0xc0 + (sse_reg << 3)); o(0xc0 + (sse_reg << 3));
} }
call_used_regs[call_used_nr_reg++] = sse_reg + TREG_XMM0;
call_used_regs[call_used_nr_reg++] = sse_reg + 1 + TREG_XMM0;
} else { } else {
assert(reg_count == 1); assert(reg_count == 1);
--sse_reg; --sse_reg;
/* Load directly to register */ /* Load directly to register */
gv(RC_XMM0 << sse_reg); gv(RC_XMM0 << sse_reg);
call_used_regs[call_used_nr_reg++] = sse_reg + TREG_XMM0;
} }
} else if (mode == x86_64_mode_integer) { } else if (mode == x86_64_mode_integer) {
/* simple type */ /* simple type */
@ -1548,10 +1612,12 @@ void gfunc_call(int nb_args)
d = arg_prepare_reg(gen_reg); d = arg_prepare_reg(gen_reg);
orex(1,d,r,0x89); /* mov */ orex(1,d,r,0x89); /* mov */
o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
call_used_regs[call_used_nr_reg++] = d;
if (reg_count == 2) { if (reg_count == 2) {
d = arg_prepare_reg(gen_reg+1); d = arg_prepare_reg(gen_reg+1);
orex(1,d,vtop->r2,0x89); /* mov */ orex(1,d,vtop->r2,0x89); /* mov */
o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d)); o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d));
call_used_regs[call_used_nr_reg++] = d;
} }
} }
vtop--; vtop--;
@ -1594,7 +1660,7 @@ void gfunc_call(int nb_args)
else if (bt == (VT_SHORT | VT_UNSIGNED)) else if (bt == (VT_SHORT | VT_UNSIGNED))
o(0xc0b70f); /* movzwl %al, %eax */ o(0xc0b70f); /* movzwl %al, %eax */
vtop--; vtop--;
in_call = 0; call_used_nr_reg = 0;
} }
@ -2386,22 +2452,31 @@ ST_FUNC void gen_vla_result(int addr) {
/* Subtract from the stack pointer, and push the resulting value onto the stack */ /* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) { ST_FUNC void gen_vla_alloc(CType *type, int align) {
#ifdef TCC_TARGET_PE int use_call = 0;
/* alloca does more than just adjust %rsp on Windows */
vpush_global_sym(&func_old_type, TOK_alloca); #if defined(CONFIG_TCC_BCHECK)
vswap(); /* Move alloca ref past allocation size */ use_call = tcc_state->do_bounds_check;
gfunc_call(1);
#else
int r;
r = gv(RC_INT); /* allocation size */
/* sub r,%rsp */
o(0x2b48);
o(0xe0 | REG_VALUE(r));
/* We align to 16 bytes rather than align */
/* and ~15, %rsp */
o(0xf0e48348);
vpop();
#endif #endif
#ifdef TCC_TARGET_PE /* alloca does more than just adjust %rsp on Windows */
use_call = 1;
#endif
if (use_call)
{
vpush_global_sym(&func_old_type, TOK_alloca);
vswap(); /* Move alloca ref past allocation size */
gfunc_call(1);
}
else {
int r;
r = gv(RC_INT); /* allocation size */
/* sub r,%rsp */
o(0x2b48);
o(0xe0 | REG_VALUE(r));
/* We align to 16 bytes rather than align */
/* and ~15, %rsp */
o(0xf0e48348);
vpop();
}
} }

View file

@ -155,7 +155,7 @@ ST_FUNC void relocate_plt(TCCState *s1)
add32le(p + 8, x - 6); add32le(p + 8, x - 6);
p += 16; p += 16;
while (p < p_end) { while (p < p_end) {
add32le(p + 2, x + s1->plt->data - p); add32le(p + 2, x + (s1->plt->data - p));
p += 16; p += 16;
} }
} }