Skip to content

Commit db71c36

Browse files
BertalanDawesomekling
authored andcommitted
Kernel: Properly align stack for signal handlers
The System V ABI requires that the stack is 16-byte aligned on function call. Confusingly, however, they mean that the stack must be aligned this way **before** the `CALL` instruction is executed. That instruction pushes the return value onto the stack, so the callee will actually see the stack pointer as a value `sizeof(FlatPtr)` smaller. The signal trampoline was written with this in mind, but `setup_stack` aligned the entire stack, *including the return address* to a 16-byte boundary. Because of this, the trampoline subtracted too much from the stack pointer, thus misaligning it. This was not a problem on i686 because we didn't execute any instructions from signal handlers that would require memory operands to be aligned to more than 4 bytes. This is not the case, however, on x86_64, where SSE instructions are enabled by default and they require 16-byte aligned operands. Running such instructions raised a GP fault, immediately killing the offending program with a SIGSEGV signal. This issue caused TestKernelAlarm to fail in LibC when ran locally, and at one point, the zsh port was affected too. Fixes #9291
1 parent b138b4c commit db71c36

File tree

1 file changed

+12
-11
lines changed

1 file changed

+12
-11
lines changed

Kernel/Thread.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -932,11 +932,11 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
932932

933933
#if ARCH(I386)
934934
// Align the stack to 16 bytes.
935-
// Note that we push 56 bytes (4 * 14) on to the stack,
936-
// so we need to account for this here.
937-
// 56 % 16 = 8, so we only need to take 8 bytes into consideration for
935+
// Note that we push 52 bytes (4 * 13) on to the stack
936+
// before the return address, so we need to account for this here.
937+
// 56 % 16 = 4, so we only need to take 4 bytes into consideration for
938938
// the stack alignment.
939-
FlatPtr stack_alignment = (stack - 8) % 16;
939+
FlatPtr stack_alignment = (stack - 4) % 16;
940940
stack -= stack_alignment;
941941

942942
push_value_on_user_stack(stack, ret_flags);
@@ -952,12 +952,12 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
952952
push_value_on_user_stack(stack, state.edi);
953953
#else
954954
// Align the stack to 16 bytes.
955-
// Note that we push 176 bytes (8 * 22) on to the stack,
956-
// so we need to account for this here.
957-
// 22 % 2 = 0, so we dont need to take anything into consideration
958-
// for the alignment.
955+
// Note that we push 168 bytes (8 * 21) on to the stack
956+
// before the return address, so we need to account for this here.
957+
// 168 % 16 = 8, so we only need to take 8 bytes into consideration for
958+
// the stack alignment.
959959
// We also are not allowed to touch the thread's red-zone of 128 bytes
960-
FlatPtr stack_alignment = stack % 16;
960+
FlatPtr stack_alignment = (stack - 8) % 16;
961961
stack -= 128 + stack_alignment;
962962

963963
push_value_on_user_stack(stack, ret_flags);
@@ -986,13 +986,14 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
986986

987987
push_value_on_user_stack(stack, signal);
988988
push_value_on_user_stack(stack, handler_vaddr.get());
989+
990+
VERIFY((stack % 16) == 0);
991+
989992
push_value_on_user_stack(stack, 0); // push fake return address
990993

991994
// We write back the adjusted stack value into the register state.
992995
// We have to do this because we can't just pass around a reference to a packed field, as it's UB.
993996
state.set_userspace_sp(stack);
994-
995-
VERIFY((stack % 16) == 0);
996997
};
997998

998999
// We now place the thread state on the userspace stack.

0 commit comments

Comments
 (0)