diff options
author | Andreas Kling <awesomekling@gmail.com> | 2020-01-12 15:04:33 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2020-01-12 15:04:33 +0100 |
commit | 8b54ba0d616acc8f26badc8f09589042b2d91552 (patch) | |
tree | 59a0109cb3ecc82b40253b8ce907833afc464083 /Kernel | |
parent | 62a191b59a8c77fc6a5d8a7255157fc273305909 (diff) | |
download | serenity-8b54ba0d616acc8f26badc8f09589042b2d91552.zip |
Kernel: Dispatch pending signals when returning from a syscall
It was quite easy to put the system into a heavy churn state by doing
e.g "cat /dev/zero".
It was then basically impossible to kill the "cat" process, even with
"kill -9", since signals are only delivered in two conditions:
a) The target thread is blocked in the kernel
b) The target thread is running in userspace
However, since "cat /dev/zero" command spends most of its time actively
running in the kernel, not blocked, the signal dispatch code just kept
postponing actually handling the signal indefinitely.
To fix this, we now check before returning from a syscall if there are
any pending unmasked signals, and if so, we take a dramatic pause by
blocking the current thread, knowing it will immediately be unblocked
by signal dispatch anyway. :^)
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Scheduler.cpp | 8 | ||||
-rw-r--r-- | Kernel/Syscall.cpp | 3 |
2 files changed, 6 insertions, 5 deletions
diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 5c78d2a5d8..18bf12e062 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -322,11 +322,9 @@ bool Scheduler::pick_next() if (&thread == current) return IterationDecision::Continue; // We know how to interrupt blocked processes, but if they are just executing - // at some random point in the kernel, let them continue. They'll be in userspace - // sooner or later and we can deliver the signal then. - // FIXME: Maybe we could check when returning from a syscall if there's a pending - // signal and dispatch it then and there? Would that be doable without the - // syscall effectively being "interrupted" despite having completed? + // at some random point in the kernel, let them continue. + // Before returning to userspace from a syscall, we will block a thread if it has any + // pending unmasked signals, allowing it to be dispatched then. if (thread.in_kernel() && !thread.is_blocked() && !thread.is_stopped()) return IterationDecision::Continue; // NOTE: dispatch_one_pending_signal() may unblock the process. diff --git a/Kernel/Syscall.cpp b/Kernel/Syscall.cpp index e4e39d6769..0a9cd38f3c 100644 --- a/Kernel/Syscall.cpp +++ b/Kernel/Syscall.cpp @@ -135,4 +135,7 @@ void syscall_handler(RegisterDump regs) // Check if we're supposed to return to userspace or just die. current->die_if_needed(); + + if (current->has_unmasked_pending_signals()) + (void)current->block<Thread::SemiPermanentBlocker>(Thread::SemiPermanentBlocker::Reason::Signal); } |