Fixture 142
nonlocal control
C · 2 functions · 4 lanes · 7 of 8 function-lanes behave identically
One lane has a function that returns a different result after decompilation: clang-O2 (1/2).
setjmp/longjmp transfers control to a frame that the ordinary CFG shows no edge to: setjmp returns twice, and longjmp discards every frame in between. The jmp_buf holds the callee-saved registers and stack pointer, so anything that assumes a single linear return path recovers this wrongly.
#include <stdint.h>
#include <setjmp.h>
/* setjmp/longjmp transfers control to a frame that the ordinary CFG shows no
* edge to: setjmp returns twice, and longjmp discards every frame in between.
* The jmp_buf holds the callee-saved registers and stack pointer, so anything
* that assumes a single linear return path recovers this wrongly. */
static void deep_thrower(jmp_buf target, int32_t depth, int32_t code) {
if (depth <= 0) {
longjmp(target, code); /* never returns */
}
/* A normal return path. Callers clamp depth to 8 so this is unreachable in
* practice, but without it every path either recurses or longjmps and GCC
* reports -Winfinite-recursion, which -Werror turns into a build failure. */
if (depth > 8) {
return;
}
deep_thrower(target, depth - 1, code);
}
__attribute__((noinline)) int32_t setjmp_returns_twice(int32_t code) {
jmp_buf target;
volatile int32_t visits = 0;
int32_t landed;
if (code < 1 || code > 8) {
return -1;
}
visits += 1;
landed = setjmp(target);
if (landed == 0) {
visits += 1;
longjmp(target, code);
}
/* Reached only on the second return, with landed == code. */
return landed * 10 + visits;
}
__attribute__((noinline)) int32_t longjmp_unwinds_frames(int32_t depth,
int32_t code) {
jmp_buf target;
int32_t landed;
if (depth < 0 || depth > 8 || code < 1 || code > 8) {
return -1;
}
landed = setjmp(target);
if (landed == 0) {
deep_thrower(target, depth, code);
return -2; /* unreachable: deep_thrower always jumps */
}
return landed * 100 + depth;
} Recovered C
Generated by glaurung decompile --style decbench at b47f6b43.
baseline.json records the result after recompiling the C and calling it beside the
original with seeded inputs.
clang -O2
1/2longjmp_unwinds_frames fail 42 lines
// glaurung: longjmp_unwinds_frames @ 0x1180
int32_t longjmp_unwinds_frames(int32_t arg0, int32_t arg1) {
extern long _setjmp(long *);
extern __attribute__((noreturn)) long longjmp(long *, long);
int landed;
long local_10;
long ret;
long rsp;
long var0;
long var10;
int var13;
long var3;
long var7;
long var8;
rsp = (rsp - 8);
rsp = (rsp - 8);
local_10 = var0;
rsp = (rsp - 200);
ret = 0xffffffff;
if (((unsigned long)(8) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
rsp = (rsp + 200);
// x86-64 epilogue: tear down frame
return ret;
}
var3 = (unsigned long)((unsigned int)(arg1));
if (((unsigned long)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) - 9)))) < (unsigned long)(0xfffffff8))) {
rsp = (rsp + 200);
// x86-64 epilogue: tear down frame
return ret;
}
var7 = (unsigned long)((unsigned int)(arg0));
var8 = _setjmp((long *)(rsp));
landed = var8;
if (((unsigned long)((unsigned int)(var8)) == 0)) {
var10 = longjmp((long *)(rsp), (unsigned long)((unsigned int)(var3)));
}
var13 = ((landed * 100) + var7);
ret = (unsigned long)((unsigned int)(var13));
rsp = (rsp + 200);
// x86-64 epilogue: tear down frame
return (unsigned int)(var13);
} setjmp_returns_twice pass 36 lines
// glaurung: setjmp_returns_twice @ 0x1120
__attribute__((no_stack_protector)) int32_t setjmp_returns_twice(int32_t arg0) {
extern long _setjmp(void *);
extern __attribute__((noreturn)) long longjmp(void *, long);
int landed;
long local_8;
unsigned char local_d8[208];
int local_dc;
long ret;
long var0;
int var12;
long var4;
long var5;
long var7;
long var9;
local_8 = var0;
local_dc = 0;
ret = 0xffffffff;
if (((unsigned long)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - 9)))) < (unsigned long)(0xfffffff8))) {
// x86-64 epilogue: tear down frame
return ret;
}
var4 = (unsigned long)((unsigned int)(arg0));
local_dc = (local_dc + 1);
var5 = _setjmp(&local_d8[0]);
landed = var5;
if (((unsigned long)((unsigned int)(var5)) == 0)) {
local_dc = (local_dc + 1);
var7 = longjmp(&local_d8[0], (unsigned long)((unsigned int)(var4)));
}
var9 = (unsigned long)((unsigned int)((landed + (landed * 4))));
var12 = ((unsigned int)((var9 + var9)) + local_dc);
ret = (unsigned long)((unsigned int)(var12));
// x86-64 epilogue: tear down frame
return (unsigned int)(var12);
} clang -O0
2/2longjmp_unwinds_frames pass 38 lines
// glaurung: longjmp_unwinds_frames @ 0x11d0
__attribute__((no_stack_protector)) int32_t longjmp_unwinds_frames(int32_t arg0, int32_t arg1) {
extern long _setjmp(void *);
extern void deep_thrower(char *, int, int);
int landed;
int local_4;
unsigned char local_e0[200];
long var0;
// x86-64 prologue: save rbp, frame 240 bytes
if (((long)(arg0) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg0)) == 8) | ((long)(arg0) < 8)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((long)(arg1) < 1)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg1)) == 8) | ((long)(arg1) < 8)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
var0 = _setjmp(&local_e0[0]);
landed = var0;
if (((unsigned long)((unsigned int)(landed)) != 0)) {
return (unsigned int)(((landed * 100) + arg0));
} else {
deep_thrower((char *)(&local_e0[0]), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
return (unsigned int)(-2);
}
} setjmp_returns_twice pass 33 lines
// glaurung: setjmp_returns_twice @ 0x1120
__attribute__((no_stack_protector)) int32_t setjmp_returns_twice(int32_t arg0) {
extern long _setjmp(void *);
extern __attribute__((noreturn)) long longjmp(void *, long);
int visits;
int landed;
int local_4;
unsigned char local_d0[200];
long var3;
long var8;
// x86-64 prologue: save rbp, frame 224 bytes
visits = 0;
if (((long)(arg0) < 1)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg0)) == 8) | ((long)(arg0) < 8)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
visits = ((unsigned int)(visits) + 1);
var3 = _setjmp(&local_d0[0]);
landed = var3;
if (((unsigned long)((unsigned int)(landed)) == 0)) {
visits = ((unsigned int)(visits) + 1);
var8 = longjmp(&local_d0[0], (unsigned long)((unsigned int)(arg0)));
}
local_4 = ((landed * 10) + (unsigned int)(visits));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} gcc -O0
2/2longjmp_unwinds_frames pass 30 lines
// glaurung: longjmp_unwinds_frames @ 0x1275
int32_t longjmp_unwinds_frames(int32_t arg0, int32_t arg1) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
extern long _setjmp(void *);
extern void deep_thrower(char *, int, int);
int landed;
long local_8;
unsigned char local_d0[200];
long ret;
long var4;
// x86-64 prologue: save rbp, frame 240 bytes
local_8 = (long)(0x28);
if ((((((long)(arg0) < 0) || ((((unsigned long)((unsigned int)(arg0)) == 8) | ((long)(arg0) < 8)) == 0)) || (((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0))) || (((unsigned long)((unsigned int)(arg1)) != 8) && (8 <= (long)(arg1))))) {
ret = 0xffffffff;
} else {
var4 = _setjmp(&local_d0[0]);
landed = var4;
if (((unsigned long)((unsigned int)(landed)) != 0)) {
ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) + ((unsigned long)((unsigned int)(landed)) * 100))));
} else {
deep_thrower((char *)(&local_d0[0]), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
ret = 0xfffffffe;
}
}
if ((local_8 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} setjmp_returns_twice pass 41 lines
// glaurung: setjmp_returns_twice @ 0x11a8
int32_t setjmp_returns_twice(int32_t arg0) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
extern long _setjmp(void *);
extern __attribute__((noreturn)) long longjmp(void *, long);
int visits;
int landed;
long local_8;
unsigned char local_d0[200];
long ret;
long var14;
int var20;
long var7;
local_8 = (long)(0x28);
visits = 0;
if (((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) == 0)) {
if ((((unsigned long)((unsigned int)(arg0)) == 8) | ((long)(arg0) < 8))) {
goto L_11ef;
}
}
ret = 0xffffffff;
goto L_125f;
L_11ef: ;
visits = ((unsigned int)(visits) + 1);
var7 = _setjmp(&local_d0[0]);
landed = var7;
if (((unsigned long)((unsigned int)(landed)) == 0)) {
visits = ((unsigned int)(visits) + 1);
var14 = longjmp(&local_d0[0], (unsigned long)((unsigned int)(arg0)));
}
var20 = ((unsigned int)(((unsigned long)((unsigned int)(landed)) << 2)) + (unsigned int)(landed));
ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(visits)) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var20)) + (unsigned long)((unsigned int)(var20))))))));
L_125f: ;
if ((local_8 == 0x28)) {
// x86-64 epilogue: restore rbp
return ret;
}
__stack_chk_fail();
// x86-64 epilogue: restore rbp
return ret;
} gcc -O2
2/2longjmp_unwinds_frames pass 37 lines
// glaurung: longjmp_unwinds_frames @ 0x1210
int32_t longjmp_unwinds_frames(int32_t arg0, int32_t arg1) {
extern long __longjmp_chk(void *, long);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
extern long _setjmp(void *);
int landed;
long local_10;
unsigned char local_d8[200];
long ret;
long var10;
long var4;
local_10 = (long)(0x28);
if (((unsigned long)(7) < (unsigned long)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) - 1)))))) {
goto L_1277;
}
if (((unsigned long)(8) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
goto L_1277;
}
var4 = _setjmp(&local_d8[0]);
landed = var4;
if (((unsigned long)((unsigned int)(var4)) == 0)) {
goto L_1280;
}
ret = (unsigned long)((unsigned int)(((landed * 100) + arg0)));
L_125c: ;
if ((local_10 != 0x28)) {
goto L_128e;
}
return ret;
L_1277: ;
ret = 0xffffffff;
goto L_125c;
L_1280: ;
var10 = __longjmp_chk(&local_d8[0], (unsigned long)((unsigned int)(arg1)));
L_128e: ;
__stack_chk_fail();
} setjmp_returns_twice pass 38 lines
// glaurung: setjmp_returns_twice @ 0x1160
int32_t setjmp_returns_twice(int32_t arg0) {
extern long __longjmp_chk(void *, long);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
extern long _setjmp(void *);
int visits;
int landed;
long local_10;
unsigned char local_d8[200];
long ret;
long var18;
long var9;
local_10 = (long)(0x28);
visits = 0;
if (((unsigned long)(7) < (unsigned long)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - 1)))))) {
goto L_11e0;
}
visits = ((unsigned int)(visits) + 1);
var9 = _setjmp(&local_d8[0]);
landed = var9;
if (((unsigned long)((unsigned int)(var9)) == 0)) {
goto L_11ec;
}
ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(visits)) + ((unsigned long)((unsigned int)((landed + (landed * 4)))) * 2))));
L_11bb: ;
if ((local_10 != 0x28)) {
goto L_11e7;
}
return ret;
L_11e0: ;
ret = 0xffffffff;
goto L_11bb;
L_11e7: ;
__stack_chk_fail();
L_11ec: ;
visits = ((unsigned int)(visits) + 1);
var18 = __longjmp_chk(&local_d8[0], (unsigned long)((unsigned int)(arg0)));
}