fix(setjmp): restore caller rbx in sigsetjmp mask path
This commit is contained in:
@@ -28,6 +28,12 @@
|
|||||||
*
|
*
|
||||||
* The mask slot is written through &env[11]; on success the flag is set and
|
* The mask slot is written through &env[11]; on success the flag is set and
|
||||||
* 0 is returned in eax, exactly like plain setjmp.
|
* 0 is returned in eax, exactly like plain setjmp.
|
||||||
|
*
|
||||||
|
* rbx carries the env base across the syscall (rdi/rsi/rdx/r10 hold the
|
||||||
|
* syscall arguments, rax the number, rcx/r11 are clobbered by the kernel).
|
||||||
|
* rbx is callee-saved: its original value was stored in env[0] above, so it
|
||||||
|
* is reloaded from there BEFORE the ret, leaving every callee-saved register
|
||||||
|
* intact on the ordinary return path as the SysV AMD64 ABI requires.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.global __sigsetjmp
|
.global __sigsetjmp
|
||||||
@@ -51,7 +57,7 @@ sigsetjmp:
|
|||||||
fnstcw 72(%rdi)
|
fnstcw 72(%rdi)
|
||||||
test %esi,%esi
|
test %esi,%esi
|
||||||
jz 1f
|
jz 1f
|
||||||
mov %rdi,%rbx /* env base (rbx is restored from the buffer later) */
|
mov %rdi,%rbx /* env base (scratch; caller rbx is in env[0]) */
|
||||||
mov $14,%eax /* SYS_rt_sigprocmask */
|
mov $14,%eax /* SYS_rt_sigprocmask */
|
||||||
xor %edi,%edi /* how = SIG_BLOCK(0) */
|
xor %edi,%edi /* how = SIG_BLOCK(0) */
|
||||||
xor %esi,%esi /* set = NULL (query only) */
|
xor %esi,%esi /* set = NULL (query only) */
|
||||||
@@ -59,6 +65,7 @@ sigsetjmp:
|
|||||||
mov $8,%r10d /* sigsetsize (x86_64 sigset_t = one word) */
|
mov $8,%r10d /* sigsetsize (x86_64 sigset_t = one word) */
|
||||||
syscall
|
syscall
|
||||||
movq $1,80(%rbx) /* mask saved: set the flag */
|
movq $1,80(%rbx) /* mask saved: set the flag */
|
||||||
|
mov 0(%rbx),%rbx /* restore the caller's rbx before returning */
|
||||||
1:
|
1:
|
||||||
xor %eax,%eax
|
xor %eax,%eax
|
||||||
ret
|
ret
|
||||||
|
|||||||
@@ -294,6 +294,30 @@ underscore_forms_test(void)
|
|||||||
check(r == 9, "_longjmp(env,9) -> _setjmp returned 9");
|
check(r == 9, "_longjmp(env,9) -> _setjmp returned 9");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 7. The initial sigsetjmp(env, 1) return must preserve the caller's rbx.
|
||||||
|
* A canary is planted in rbx via inline assembly (declared clobbered, so
|
||||||
|
* the compiler holds nothing else there), sigsetjmp returns normally, and
|
||||||
|
* rbx is read back raw: the SysV AMD64 ABI requires the function to leave
|
||||||
|
* every callee-saved register intact on the ordinary return path, and the
|
||||||
|
* compiler assumes exactly that when allocating registers around the call,
|
||||||
|
* so nothing reloads rbx between the plant and the read.
|
||||||
|
*/
|
||||||
|
static __attribute__((noinline)) void
|
||||||
|
rbx_callee_saved_test(void)
|
||||||
|
{
|
||||||
|
sigjmp_buf env;
|
||||||
|
unsigned long after = 0;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
__asm__ volatile("mov $0x12345678, %%rbx" ::: "rbx");
|
||||||
|
r = sigsetjmp(env, 1);
|
||||||
|
__asm__ volatile("mov %%rbx, %0" : "=r"(after));
|
||||||
|
|
||||||
|
check(r == 0, "sigsetjmp(env,1) initial return is 0 (rbx canary scenario)");
|
||||||
|
check(after == 0x12345678UL, "caller rbx preserved across sigsetjmp(env,1) initial return");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Failure scenario (-f): longjmp(env, 0) must make setjmp return 1. Exits 0
|
* Failure scenario (-f): longjmp(env, 0) must make setjmp return 1. Exits 0
|
||||||
* only when the coercion behaved exactly as POSIX specifies.
|
* only when the coercion behaved exactly as POSIX specifies.
|
||||||
@@ -334,6 +358,7 @@ main(int argc, char **argv)
|
|||||||
fp_state_test();
|
fp_state_test();
|
||||||
sigmask_test();
|
sigmask_test();
|
||||||
underscore_forms_test();
|
underscore_forms_test();
|
||||||
|
rbx_callee_saved_test();
|
||||||
|
|
||||||
if (failures > 0)
|
if (failures > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user