summaryrefslogtreecommitdiff
path: root/Kernel/Thread.cpp
diff options
context:
space:
mode:
authorDaniel Bertalan <dani@danielbertalan.dev>2021-08-01 20:30:43 +0200
committerAndreas Kling <kling@serenityos.org>2021-08-08 10:55:36 +0200
commitfa8507d1ceba8604626caa68fdbdb3a2086562e9 (patch)
treeb99d4a3e6ceebdf3c75eafd76fb2ed1a7339c304 /Kernel/Thread.cpp
parent90caebe96afe5e9d8fde9abc22ab9aa3d5a36968 (diff)
downloadserenity-fa8507d1ceba8604626caa68fdbdb3a2086562e9.zip
Kernel: Fix UB caused by taking a reference to a packed struct's member
Taking a reference or a pointer to a value that's not aligned properly is undefined behavior. While `[[gnu::packed]]` ensures that reads from and writes to fields of packed structs is a safe operation, the information about the reduced alignment is lost when creating pointers to these values. Weirdly enough, GCC's undefined behavior sanitizer doesn't flag these, even though the doc of `-Waddress-of-packed-member` says that it usually leads to UB. In contrast, x86_64 Clang does flag these, which renders the 64-bit kernel unable to boot. For now, the `address-of-packed-member` warning will only be enabled in the kernel, as it is absolutely crucial there because of KUBSAN, but might get excessively noisy for the userland in the future. Also note that we can't append to `CMAKE_CXX_FLAGS` like we do for other flags in the kernel, because flags added via `add_compile_options` come after these, so the `-Wno-address-of-packed-member` in the root would cancel it out.
Diffstat (limited to 'Kernel/Thread.cpp')
-rw-r--r--Kernel/Thread.cpp32
1 files changed, 20 insertions, 12 deletions
diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp
index 2cdc181cf2..efc76f2d6a 100644
--- a/Kernel/Thread.cpp
+++ b/Kernel/Thread.cpp
@@ -824,10 +824,10 @@ bool Thread::has_signal_handler(u8 signal) const
return !action.handler_or_sigaction.is_null();
}
-static bool push_value_on_user_stack(FlatPtr* stack, FlatPtr data)
+static bool push_value_on_user_stack(FlatPtr& stack, FlatPtr data)
{
- *stack -= sizeof(FlatPtr);
- return copy_to_user((FlatPtr*)*stack, &data);
+ stack -= sizeof(FlatPtr);
+ return copy_to_user((FlatPtr*)stack, &data);
}
void Thread::resume_from_stopped()
@@ -946,15 +946,15 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
auto setup_stack = [&](RegisterState& state) {
#if ARCH(I386)
- FlatPtr* stack = &state.userspace_esp;
- FlatPtr old_esp = *stack;
+ FlatPtr stack = state.userspace_esp;
+ FlatPtr old_esp = stack;
FlatPtr ret_eip = state.eip;
FlatPtr ret_eflags = state.eflags;
dbgln_if(SIGNAL_DEBUG, "Setting up user stack to return to EIP {:p}, ESP {:p}", ret_eip, old_esp);
#elif ARCH(X86_64)
- FlatPtr* stack = &state.userspace_rsp;
- FlatPtr old_rsp = *stack;
+ FlatPtr stack = state.userspace_rsp;
+ FlatPtr old_rsp = stack;
FlatPtr ret_rip = state.rip;
FlatPtr ret_rflags = state.rflags;
@@ -967,8 +967,8 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
// so we need to account for this here.
// 56 % 16 = 8, so we only need to take 8 bytes into consideration for
// the stack alignment.
- FlatPtr stack_alignment = (*stack - 8) % 16;
- *stack -= stack_alignment;
+ FlatPtr stack_alignment = (stack - 8) % 16;
+ stack -= stack_alignment;
push_value_on_user_stack(stack, ret_eflags);
@@ -988,8 +988,8 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
// 22 % 2 = 0, so we dont need to take anything into consideration
// for the alignment.
// We also are not allowed to touch the thread's red-zone of 128 bytes
- FlatPtr stack_alignment = *stack % 16;
- *stack -= 128 + stack_alignment;
+ FlatPtr stack_alignment = stack % 16;
+ stack -= 128 + stack_alignment;
push_value_on_user_stack(stack, ret_rflags);
@@ -1019,7 +1019,15 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
push_value_on_user_stack(stack, handler_vaddr.get());
push_value_on_user_stack(stack, 0); // push fake return address
- VERIFY((*stack % 16) == 0);
+ // We write back the adjusted stack value into the register state.
+ // We have to do this because we can't just pass around a reference to a packed field, as it's UB.
+#if ARCH(I386)
+ state.userspace_esp = stack;
+#else
+ state.userspace_rsp = stack;
+#endif
+
+ VERIFY((stack % 16) == 0);
};
// We now place the thread state on the userspace stack.