From 50899e30abf3cbd71a09ecc03b2e172f6ba5d4f4 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Mon, 9 Mar 2015 00:19:59 +0100 Subject: [PATCH] Fix stack overwrite on structure return The common code to move a returned structure packed into registers into memory on the caller side didn't take the register size into account when allocating local storage, so sometimes that lead to stack overwrites (e.g. in 73_arm64.c), on x86_64. This fixes it by generally making gfunc_sret also return the register size. --- arm-gen.c | 8 +++++--- arm64-gen.c | 2 +- c67-gen.c | 2 +- i386-gen.c | 3 ++- tcc.h | 2 +- tccgen.c | 20 ++++++++++++-------- x86_64-gen.c | 6 ++++-- 7 files changed, 26 insertions(+), 17 deletions(-) diff --git a/arm-gen.c b/arm-gen.c index 3bb5326c..0201cd2f 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -869,18 +869,20 @@ int floats_in_core_regs(SValue *sval) /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) { +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { #ifdef TCC_ARM_EABI int size, align; size = type_size(vt, &align); if (float_abi == ARM_HARD_FLOAT && !variadic && (is_float(vt->t) || is_hgen_float_aggr(vt))) { *ret_align = 8; + *regsize = 8; ret->ref = NULL; ret->t = VT_DOUBLE; return (size + 7) >> 3; } else if (size <= 4) { *ret_align = 4; + *regsize = 4; ret->ref = NULL; ret->t = VT_INT; return 1; @@ -1255,7 +1257,7 @@ void gfunc_call(int nb_args) void gfunc_prolog(CType *func_type) { Sym *sym,*sym2; - int n, nf, size, align, struct_ret = 0; + int n, nf, size, align, rs, struct_ret = 0; int addr, pn, sn; /* pn=core, sn=stack */ CType ret_type; @@ -1269,7 +1271,7 @@ void gfunc_prolog(CType *func_type) n = nf = 0; if ((func_vt.t & VT_BTYPE) == VT_STRUCT && - !gfunc_sret(&func_vt, func_var, &ret_type, &align)) + !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs)) { n++; struct_ret = 1; diff --git a/arm64-gen.c b/arm64-gen.c index 3581e5d3..0c435d9a 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -1196,7 +1196,7 @@ ST_FUNC void gen_va_arg(CType *t) } } -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize) { return 0; } diff --git a/c67-gen.c b/c67-gen.c index 5a6fd568..21825183 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -1881,7 +1881,7 @@ static void gcall_or_jmp(int is_jmp) /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) { +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { *ret_align = 1; // Never have to re-align return values for x86-64 return 0; } diff --git a/i386-gen.c b/i386-gen.c index 9bdf9980..c2db3d0a 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -376,12 +376,13 @@ static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { #ifdef TCC_TARGET_PE int size, align; *ret_align = 1; // Never have to re-align return values for x86 + *regsize = 4; size = type_size(vt, &align); if (size > 8) { return 0; diff --git a/tcc.h b/tcc.h index 7d53b9b1..759d3f4f 100644 --- a/tcc.h +++ b/tcc.h @@ -1327,7 +1327,7 @@ ST_FUNC void gsym_addr(int t, int a); ST_FUNC void gsym(int t); ST_FUNC void load(int r, SValue *sv); ST_FUNC void store(int r, SValue *v); -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align); +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize); ST_FUNC void gfunc_call(int nb_args); ST_FUNC void gfunc_prolog(CType *func_type); ST_FUNC void gfunc_epilog(void); diff --git a/tccgen.c b/tccgen.c index 52541e29..58c8b13c 100644 --- a/tccgen.c +++ b/tccgen.c @@ -4153,7 +4153,7 @@ ST_FUNC void unary(void) } else if (tok == '(') { SValue ret; Sym *sa; - int nb_args, ret_nregs, ret_align, variadic; + int nb_args, ret_nregs, ret_align, regsize, variadic; /* function call */ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { @@ -4179,7 +4179,7 @@ ST_FUNC void unary(void) if ((s->type.t & VT_BTYPE) == VT_STRUCT) { variadic = (s->c == FUNC_ELLIPSIS); ret_nregs = gfunc_sret(&s->type, variadic, &ret.type, - &ret_align); + &ret_align, ®size); if (!ret_nregs) { /* get some space for the returned structure */ size = type_size(&s->type, &align); @@ -4259,6 +4259,10 @@ ST_FUNC void unary(void) int addr, offset; size = type_size(&s->type, &align); + /* We're writing whole regs often, make sure there's enough + space. Assume register size is power of 2. */ + if (regsize > align) + align = regsize; loc = (loc - size) & -align; addr = loc; offset = 0; @@ -4269,8 +4273,7 @@ ST_FUNC void unary(void) vtop--; if (--ret_nregs == 0) break; - /* XXX: compatible with arm only: ret_align == register_size */ - offset += ret_align; + offset += regsize; } vset(&s->type, VT_LOCAL | VT_LVAL, addr); } @@ -4850,9 +4853,9 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, #else if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { CType type, ret_type; - int ret_align, ret_nregs; + int ret_align, ret_nregs, regsize; ret_nregs = gfunc_sret(&func_vt, func_var, &ret_type, - &ret_align); + &ret_align, ®size); if (0 == ret_nregs) { /* if returning structure, must copy it to implicit first pointer arg location */ @@ -4890,9 +4893,10 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, /* We assume that when a structure is returned in multiple registers, their classes are consecutive values of the suite s(n) = 2^n */ + /* XXX This seems confused, if r == 0, this never + changes r. */ r <<= 1; - /* XXX: compatible with arm only: ret_align == register_size */ - vtop->c.i += ret_align; + vtop->c.i += regsize; vtop->r = VT_LOCAL | VT_LVAL; } } diff --git a/x86_64-gen.c b/x86_64-gen.c index 67aaadc7..d5ed4f67 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -662,9 +662,10 @@ void gen_offs_sp(int b, int r, int d) /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { int size, align; + *regsize = 8; *ret_align = 1; // Never have to re-align return values for x86-64 size = type_size(vt, &align); ret->ref = NULL; @@ -1069,10 +1070,11 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { int size, align, reg_count; *ret_align = 1; // Never have to re-align return values for x86-64 + *regsize = 8; return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory); } -- 2.11.4.GIT