Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

You don't need to pass it by address to a function. Something like this could trigger it:

    // region 1:
    // x is allocated to register r1
    int x = 0; // store 0 to r1
    jmpbuf jb;
    if (setjmp(jb) == 0) {
        // region 2:
        // x is re-allocated to r2, so r1 is copied to r2
        x = 42; // store 42 to r2
        longjmp(jb, 1);
    }
    // region 3:
    // x is allocated to r1
    printf("%d\n", x); // load from r1
The compiler is allowed to allocate the same variable to different locations at different points in the program. At re-allocation boundaries, it will insert register moves or stack spills or whatever. The problem in the example is that the longjmp() crosses a re-allocation barrier, so the store to r2 won't be registered as a store to x in region 3.

Actually, this would still occur even if x was always allocated to r1. The reason is that before calling setjmp() the compiler spills x to the stack, so the callee can use the registers for its own use, and on returning (either via longjmp() or the first time through) it will restore x from the value on the stack.

I tried the example above with GCC. At the default optimization level it printed 42. But with -O2 it printed 0. Looking at the assembly code, it's actually even worse than I described. The compiler has treated the longjmp() akin to an exit() and so has determined that the x = 42 is actually a no-op that can be eliminated. This is similar to how in printf("1"); exit(0); printf("2"); the compiler will actually remove the second printf() as dead code from the executable.

So, in conclusion, there seems to be a whole bunch of ways in which the compiler could screw this up while staying within the bounds of standards-acceptable behavior.



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: