From e88575dbac2075d574d65bcc91f0e345ee2110d2 Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Fri, 14 Aug 2015 11:38:55 +0000 Subject: RISC OS: add patch to fix stack corruption Given a function such as this: void foo(void) { register unsigned int sp __asm__("sp"); _exit(sp); } GCCSDK 4.7.4 release 1 will generate the following output when the optimiser is enabled (it doesn't matter which optimisation level is chosen, so long as it's >0): mov ip, sp stmfd sp!, {fp, ip, lr, pc} cmp sp, sl bllt __rt_stkovf_split_small mov r0, sp bl _exit If this function is called from a parent that has caused the current stack chunk to be fully utilised (i.e. SP on entry to foo is less than SL), then the stack chunk extender will be called. __rt_stkovf_split_small will replace the return address of the current stack frame with the address of the stack chunk cleanup function (that foo is effectively noreturn doesn't matter here). The real return address is squirreled away in a field at the base of the new stack chunk, and will be retrieved by the cleanup code. In the function prologue emitted above, however, the frame pointer is not updated before the stack check is performed. The result is that the *parent* function's stack frame will be modified instead. This causes much badness as the parent function is using a completely different stack chunk and so, when it returns to its parent, we will very likely branch through zero (if the parent's stack chunk is the initial chunk) or return to some unexpected place further up the call stack, most likely with the wrong result values (if the parent's stack chunk is not the initial chunk) To fix this, we mark rt_stkovf_v5_clobbered as using r11 (fp), in much the same way as rt_stkovf already does. This prevents the peephole optimiser optimising out the frame pointer update. This results in this much more sensible output: mov ip, sp stmfd sp!, {fp, ip, lr, pc} sub fp, ip, #4 cmp sp, sl bllt __rt_stkovf_split_small mov r0, sp bl _exit This ensures that the correct stack frame is modified by __rt_stkovf_split_small. Note that, in this particular case, foo does not return, so the stack chunk cleaning won't happen. This isn't really a problem, as the only real ways out of functions which do not return are process exit, or longjmp which, in the UnixLib implementation, explicitly cleans up stack chunks before returning control to the location specified in the jmp_buf. --- arm-unknown-riscos/recipes/patches/gcc4/riscos.md.p | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 arm-unknown-riscos/recipes/patches/gcc4/riscos.md.p diff --git a/arm-unknown-riscos/recipes/patches/gcc4/riscos.md.p b/arm-unknown-riscos/recipes/patches/gcc4/riscos.md.p new file mode 100644 index 0000000..9c5e9f1 --- /dev/null +++ b/arm-unknown-riscos/recipes/patches/gcc4/riscos.md.p @@ -0,0 +1,10 @@ +--- recipe/files/gcc/gcc/config/arm/riscos.md (revision 6894) ++++ recipe/files/gcc/gcc/config/arm/riscos.md (working copy) +@@ -127,6 +127,7 @@ + (match_operand 3 "" "")] UNSPEC_STK) + (clobber (reg:SI 8)) + (clobber (reg:SI SL_REGNUM)) ++ (use (reg:SI 11)) + (clobber (reg:SI IP_REGNUM)) + (clobber (reg:SI LR_REGNUM)) + (clobber (reg:CC CC_REGNUM))] -- cgit v1.2.3