GCC reuses stack slot in setjmp-calling function -- is it a bug or feature?
Mikael Pettersson
mikpe@it.uu.se
Mon Apr 15 11:54:00 GMT 2013
Konstantin Vladimirov writes:
> Hi,
>
> Minimal reproduction for x86:
>
> #include <setjmp.h>
> #include <stdio.h>
> #include <stdlib.h>
>
> jmp_buf something_happened;
> int key_gdata = 2;
>
> int store_data;
>
> void __attribute__((noinline))
> initiate(void)
> {
> longjmp(something_happened, 1);
> }
>
> int __attribute__ ((noinline))
> foo(int x)
> {
> return x + 1;
> }
>
> int __attribute__((noinline))
> amibuggy(int x, int y1, int y2, int y3)
> {
> int nextdata, idx, key_data;
>
> key_data = key_gdata;
>
> if (setjmp(something_happened))
> {
> if (key_data != key_gdata)
> {
> fprintf(stderr, "Test failed: data = %d, g_data = %d\n",
> key_data, key_gdata);
> abort();
> }
> else
> {
> fprintf(stdout, "Test passed\n");
> fprintf(stdout, "data = %d, g_data = %d\n", key_data, key_gdata);
> return 0;
> }
> }
>
> store_data = key_data;
> nextdata = key_gdata;
>
> idx = foo(nextdata);
>
> for (;idx != 0; --idx)
> {
> int tmp = store_data;
> nextdata += foo(tmp);
> store_data = foo(nextdata);
> }
>
> initiate();
> return 0;
> }
>
> int
> main(void)
> {
> amibuggy(9, 5, 16, 36);
> amibuggy(0, 0, 0, 0);
> return 0;
> }
>
>
> Compile with gcc-4.7.2 with line:
>
> gcc -O2 repro.c -o repro.x86
>
> Now run and you will see test "magically" failed.
>
> What happens? Compiler on register allocation pass decided to reuse
> stack slot 12(%rsp) where key_data persisted.
>
> movl key_gdata(%rip), %eax
> movl %eax, 12(%rsp)
> call _setjmp
>
> ... and later ...
>
> .L10:
> movl 12(%rsp), %edi <--- oops!
> call foo
>
> setjmp stored stack pointer, next frame slot is overwritten by
> spill/fill (because compiler thinks that key_data is already dead at
> this point. Then everything explodes.
>
> I am not sure that it is a bug, but situation is rather complicated
> for programmer -- I think in this case programmer really may rely on
> context restoring after longjmp, and surprise is really great.
>
> Question to gcc-help: Please comment on should I file this in
> bugzilla, or should I live with it.
>
> Question to gcc: Really I found this problem in private backend and
> want to fix it for my users, no matter first answer is yes or no.
> Maybe somehow restrict stack slot reusage if function calls setjmp.
> But I am not so good in reload internals. Any advices about how to
> locally fix it in private backend sources (I haven't still done any
> changes to normal gcc-4.7.2, just added new machine description) are
> very appreciated.
You need to read the C standard and understand the restrictions it
places on the values of non-volatile automatic variables when setjmp
returns due to a longjmp. Look for "The longjmp function" in
WG14/n1124.pdf or n1494.pdf.
Then you'll need to use "volatile" or restructure your code slightly.
More information about the Gcc
mailing list