diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2019-12-17 16:44:52 -0800 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2019-12-17 16:44:52 -0800 |
commit | 1325adcc2219a2052bb646deea2080f1d76364d9 (patch) | |
tree | 443eea3fedbd65ec45568380ecae2f5d909aebf5 /strudel.c | |
parent | b8f8be2738e304f1b857b0c1be7abe77f1c622e7 (diff) | |
download | txr-1325adcc2219a2052bb646deea2080f1d76364d9.tar.gz txr-1325adcc2219a2052bb646deea2080f1d76364d9.tar.bz2 txr-1325adcc2219a2052bb646deea2080f1d76364d9.zip |
bugfix: crash in extended_setjmp due to PIE.
A crash occurs on Ubuntu 18.04, 32 bit x86, when executing the
test case tests/007/except-2.txr, whereby TXR segfaults in the
v_try function.
This is reminiscent of a January, 2016 report in the txr-users
mailing list from Morit Barsnick, who also ran into a crash in
the same test case.
Background: it appears that the compiler in Ubuntu 18.04
enables PIE (position-independent executables) by default. Thus
even simple executables that are not shared libraries
reference their own global variables through an offset table,
instead of direct addressing. To access globals, the compiler
has to emit code that retrieves their addresses from a table,
pulling them into a register, and then performing indirect
memory accesses through the register. Sometimes the emitted
code doesn't keep these addresses in a register. The address
of a global variable accessed multiple times in a block of
code may get spilled from a register into the stack, and then
later retrieved from the stack again to access that same
global.
In our extended_setjmp logic, we save the values of a few
global variables and restore them if the extended_longjmp
takes place to return to that point. The problem is that when
restoring some of the globals, the compiler is relying on
retrieving the effective addresses from the temporary spill
locations in the stack. However, those temporary locations
have since been re-used for other purposes and the access to
the globals therefore crashes or produces unpredictable
results.
Essentially, it's as if GCC did this around our code:
{
unsigned *debug_enable_addr = &debug_enable;
/* save and restore logic here uses *debug_enable_addr
* to refer to debug_enable
*/
if (extended_setjmp(...))
...
}
/* Oops, debug_enable_addr is now garbage!
* We are jumping back into the scope which will try to use
* its value to restore the debug_enable global.
*/
extended_longjmp(...);
I have experimented with a few approaches that did not work, and settled
on moving the code which saves and restores the globals into functions.
GCC will not cache the effective address calculation of a global
variable access between calls to different external functions which
access that variable.
The mitigation in this commit gets the test cases to pass even
if TXR is compiled with PIE. However, PIE should be disabled.
Not only does it cause the above problem, but it has a huge
performance impact: a more than 16% slowdown, which is quite
unacceptable.
* eval.h (dyn_env): Delare here. Some sources were depending on signal.h
providing this, which is wrong. Now signal.h doesn't declare it any
longer.
* signal.h (EJ_DBG_SAVE, EJ_DBG_REST): Macros removed.
(extended_setjmp): Greatly simplified. Extended restoring logic is now
done in extended_longjmp, and the extended save for the globals
is a function call. Just moving the restore into extended_longjmp
probably would have fixed this issue.
(extended_longjmp): Call extjmp_restore.
(extjmp_save, extjmp_restore): Declared.
* unwind.c (extjmp_save, extjmp_restore): New functions.
Diffstat (limited to 'strudel.c')
0 files changed, 0 insertions, 0 deletions