mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 23:17:45 +00:00
Kernel: Don't let signals unblock threads while handling a page fault
It was possible to signal a process while it was paging in an inode backed VM object. This would cause the inode read to EINTR, and the page fault handler would assert. Solve this by simply not unblocking threads due to signals if they are currently busy handling a page fault. This is probably not the best way to solve this issue, so I've added a FIXME to that effect.
This commit is contained in:
parent
1f53dd0943
commit
928ee2c791
3 changed files with 21 additions and 0 deletions
|
@ -25,6 +25,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Assertions.h>
|
#include <AK/Assertions.h>
|
||||||
|
#include <AK/ScopeGuard.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
@ -257,6 +258,14 @@ void page_fault_handler(TrapFrame* trap)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto current_thread = Thread::current();
|
auto current_thread = Thread::current();
|
||||||
|
|
||||||
|
if (current_thread)
|
||||||
|
current_thread->set_handling_page_fault(true);
|
||||||
|
ScopeGuard guard = [current_thread] {
|
||||||
|
if (current_thread)
|
||||||
|
current_thread->set_handling_page_fault(false);
|
||||||
|
};
|
||||||
|
|
||||||
if (!faulted_in_kernel && !MM.validate_user_stack(current_thread->process(), VirtualAddress(regs.userspace_esp))) {
|
if (!faulted_in_kernel && !MM.validate_user_stack(current_thread->process(), VirtualAddress(regs.userspace_esp))) {
|
||||||
dbgln("Invalid stack pointer: {}", VirtualAddress(regs.userspace_esp));
|
dbgln("Invalid stack pointer: {}", VirtualAddress(regs.userspace_esp));
|
||||||
handle_crash(regs, "Bad stack on page fault", SIGSTKFLT);
|
handle_crash(regs, "Bad stack on page fault", SIGSTKFLT);
|
||||||
|
|
|
@ -166,6 +166,12 @@ void Thread::unblock(u8 signal)
|
||||||
return;
|
return;
|
||||||
ASSERT(m_blocker);
|
ASSERT(m_blocker);
|
||||||
if (signal != 0) {
|
if (signal != 0) {
|
||||||
|
if (is_handling_page_fault()) {
|
||||||
|
// Don't let signals unblock threads that are blocked inside a page fault handler.
|
||||||
|
// This prevents threads from EINTR'ing the inode read in an inode page fault.
|
||||||
|
// FIXME: There's probably a better way to solve this.
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!m_blocker->can_be_interrupted() && !m_should_die)
|
if (!m_blocker->can_be_interrupted() && !m_should_die)
|
||||||
return;
|
return;
|
||||||
m_blocker->set_interrupted_by_signal(signal);
|
m_blocker->set_interrupted_by_signal(signal);
|
||||||
|
@ -461,6 +467,8 @@ u32 Thread::pending_signals_for_state() const
|
||||||
{
|
{
|
||||||
ASSERT(g_scheduler_lock.own_lock());
|
ASSERT(g_scheduler_lock.own_lock());
|
||||||
constexpr u32 stopped_signal_mask = (1 << (SIGCONT - 1)) | (1 << (SIGKILL - 1)) | (1 << (SIGTRAP - 1));
|
constexpr u32 stopped_signal_mask = (1 << (SIGCONT - 1)) | (1 << (SIGKILL - 1)) | (1 << (SIGTRAP - 1));
|
||||||
|
if (is_handling_page_fault())
|
||||||
|
return 0;
|
||||||
return m_state != Stopped ? m_pending_signals : m_pending_signals & stopped_signal_mask;
|
return m_state != Stopped ? m_pending_signals : m_pending_signals & stopped_signal_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1155,6 +1155,9 @@ public:
|
||||||
return m_kernel_stack_region;
|
return m_kernel_stack_region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_handling_page_fault() const { return m_handling_page_fault; }
|
||||||
|
void set_handling_page_fault(bool b) { m_handling_page_fault = b; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IntrusiveListNode m_runnable_list_node;
|
IntrusiveListNode m_runnable_list_node;
|
||||||
|
|
||||||
|
@ -1258,6 +1261,7 @@ private:
|
||||||
JoinBlockCondition m_join_condition;
|
JoinBlockCondition m_join_condition;
|
||||||
Atomic<bool> m_is_active { false };
|
Atomic<bool> m_is_active { false };
|
||||||
bool m_is_joinable { true };
|
bool m_is_joinable { true };
|
||||||
|
bool m_handling_page_fault { false };
|
||||||
|
|
||||||
unsigned m_syscall_count { 0 };
|
unsigned m_syscall_count { 0 };
|
||||||
unsigned m_inode_faults { 0 };
|
unsigned m_inode_faults { 0 };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue