[gccsdk] [PATCH] Fix stack frame corruption

John-Mark Bell jmb at netsurf-browser.org
Fri Aug 14 09:34:36 PDT 2015


All,

The NetSurf team have recently discovered an interaction
between stack checking and non-leaf functions which do
not return. In our specific case, the function which does
not return actually calls longjmp, and the result is a
branch through zero later on in program execution.

Details are below, and I've attached a patch that resolves
this. Assuming I can remember my SVN credentials, I'm happy
to commit it myself but, as I'm rather out of touch with
goings on in these parts, and I see you're preparing a
release, I don't want to trample on toes unnecessarily!

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.


J.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: stack-extension.patch
Type: text/x-patch
Size: 486 bytes
Desc: not available
URL: <http://www.riscos.info/pipermail/gcc/attachments/20150814/894c0143/attachment.bin>


More information about the gcc mailing list