1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 22:17:42 +00:00

Kernel: Handle invalid stack pointer during signal dispatch

Instead of crashing the kernel, we simply terminate the process.
This commit is contained in:
Idan Horowitz 2021-11-30 01:21:03 +02:00
parent 40f64d7379
commit 711a7104f3

View file

@ -819,11 +819,10 @@ bool Thread::is_in_alternative_signal_stack() const
return sp >= m_alternative_signal_stack && sp < m_alternative_signal_stack + m_alternative_signal_stack_size; return sp >= m_alternative_signal_stack && sp < m_alternative_signal_stack + m_alternative_signal_stack_size;
} }
static void push_value_on_user_stack(FlatPtr& stack, FlatPtr data) static ErrorOr<void> push_value_on_user_stack(FlatPtr& stack, FlatPtr data)
{ {
stack -= sizeof(FlatPtr); stack -= sizeof(FlatPtr);
auto result = copy_to_user((FlatPtr*)stack, &data); return copy_to_user((FlatPtr*)stack, &data);
VERIFY(!result.is_error());
} }
void Thread::resume_from_stopped() void Thread::resume_from_stopped()
@ -942,7 +941,7 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
bool use_alternative_stack = ((action.flags & SA_ONSTACK) != 0) && has_alternative_signal_stack() && !is_in_alternative_signal_stack(); bool use_alternative_stack = ((action.flags & SA_ONSTACK) != 0) && has_alternative_signal_stack() && !is_in_alternative_signal_stack();
auto setup_stack = [&](RegisterState& state) { auto setup_stack = [&](RegisterState& state) -> ErrorOr<void> {
FlatPtr old_sp = state.userspace_sp(); FlatPtr old_sp = state.userspace_sp();
FlatPtr stack; FlatPtr stack;
if (use_alternative_stack) if (use_alternative_stack)
@ -964,17 +963,17 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
FlatPtr stack_alignment = (stack - 4) % 16; FlatPtr stack_alignment = (stack - 4) % 16;
stack -= stack_alignment; stack -= stack_alignment;
push_value_on_user_stack(stack, ret_flags); TRY(push_value_on_user_stack(stack, ret_flags));
push_value_on_user_stack(stack, ret_ip); TRY(push_value_on_user_stack(stack, ret_ip));
push_value_on_user_stack(stack, state.eax); TRY(push_value_on_user_stack(stack, state.eax));
push_value_on_user_stack(stack, state.ecx); TRY(push_value_on_user_stack(stack, state.ecx));
push_value_on_user_stack(stack, state.edx); TRY(push_value_on_user_stack(stack, state.edx));
push_value_on_user_stack(stack, state.ebx); TRY(push_value_on_user_stack(stack, state.ebx));
push_value_on_user_stack(stack, old_sp); TRY(push_value_on_user_stack(stack, old_sp));
push_value_on_user_stack(stack, state.ebp); TRY(push_value_on_user_stack(stack, state.ebp));
push_value_on_user_stack(stack, state.esi); TRY(push_value_on_user_stack(stack, state.esi));
push_value_on_user_stack(stack, state.edi); TRY(push_value_on_user_stack(stack, state.edi));
#else #else
// Align the stack to 16 bytes. // Align the stack to 16 bytes.
// Note that we push 168 bytes (8 * 21) on to the stack // Note that we push 168 bytes (8 * 21) on to the stack
@ -985,40 +984,42 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
FlatPtr stack_alignment = (stack - 8) % 16; FlatPtr stack_alignment = (stack - 8) % 16;
stack -= 128 + stack_alignment; stack -= 128 + stack_alignment;
push_value_on_user_stack(stack, ret_flags); TRY(push_value_on_user_stack(stack, ret_flags));
push_value_on_user_stack(stack, ret_ip); TRY(push_value_on_user_stack(stack, ret_ip));
push_value_on_user_stack(stack, state.r15); TRY(push_value_on_user_stack(stack, state.r15));
push_value_on_user_stack(stack, state.r14); TRY(push_value_on_user_stack(stack, state.r14));
push_value_on_user_stack(stack, state.r13); TRY(push_value_on_user_stack(stack, state.r13));
push_value_on_user_stack(stack, state.r12); TRY(push_value_on_user_stack(stack, state.r12));
push_value_on_user_stack(stack, state.r11); TRY(push_value_on_user_stack(stack, state.r11));
push_value_on_user_stack(stack, state.r10); TRY(push_value_on_user_stack(stack, state.r10));
push_value_on_user_stack(stack, state.r9); TRY(push_value_on_user_stack(stack, state.r9));
push_value_on_user_stack(stack, state.r8); TRY(push_value_on_user_stack(stack, state.r8));
push_value_on_user_stack(stack, state.rax); TRY(push_value_on_user_stack(stack, state.rax));
push_value_on_user_stack(stack, state.rcx); TRY(push_value_on_user_stack(stack, state.rcx));
push_value_on_user_stack(stack, state.rdx); TRY(push_value_on_user_stack(stack, state.rdx));
push_value_on_user_stack(stack, state.rbx); TRY(push_value_on_user_stack(stack, state.rbx));
push_value_on_user_stack(stack, old_sp); TRY(push_value_on_user_stack(stack, old_sp));
push_value_on_user_stack(stack, state.rbp); TRY(push_value_on_user_stack(stack, state.rbp));
push_value_on_user_stack(stack, state.rsi); TRY(push_value_on_user_stack(stack, state.rsi));
push_value_on_user_stack(stack, state.rdi); TRY(push_value_on_user_stack(stack, state.rdi));
#endif #endif
// PUSH old_signal_mask // PUSH old_signal_mask
push_value_on_user_stack(stack, old_signal_mask); TRY(push_value_on_user_stack(stack, old_signal_mask));
push_value_on_user_stack(stack, signal); TRY(push_value_on_user_stack(stack, signal));
push_value_on_user_stack(stack, handler_vaddr.get()); TRY(push_value_on_user_stack(stack, handler_vaddr.get()));
VERIFY((stack % 16) == 0); VERIFY((stack % 16) == 0);
push_value_on_user_stack(stack, 0); // push fake return address TRY(push_value_on_user_stack(stack, 0)); // push fake return address
// We write back the adjusted stack value into the register state. // 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. // We have to do this because we can't just pass around a reference to a packed field, as it's UB.
state.set_userspace_sp(stack); state.set_userspace_sp(stack);
return {};
}; };
// We now place the thread state on the userspace stack. // We now place the thread state on the userspace stack.
@ -1026,7 +1027,18 @@ DispatchSignalResult Thread::dispatch_signal(u8 signal)
// Conversely, when the thread isn't blocking the RegisterState may not be // Conversely, when the thread isn't blocking the RegisterState may not be
// valid (fork, exec etc) but the tss will, so we use that instead. // valid (fork, exec etc) but the tss will, so we use that instead.
auto& regs = get_register_dump_from_stack(); auto& regs = get_register_dump_from_stack();
setup_stack(regs);
auto result = setup_stack(regs);
if (result.is_error()) {
dbgln("Invalid stack pointer: {}", regs.userspace_sp());
process.set_should_generate_coredump(true);
process.for_each_thread([](auto& thread) {
thread.set_dump_backtrace_on_finalization();
});
m_process->terminate_due_to_signal(signal);
return DispatchSignalResult::Terminate;
}
auto signal_trampoline_addr = process.signal_trampoline().get(); auto signal_trampoline_addr = process.signal_trampoline().get();
regs.set_ip(signal_trampoline_addr); regs.set_ip(signal_trampoline_addr);