Kernel: Block a signal from being dispatched again until handler returns.

We don't handle nesting yet, but this is a step in the right direction.
This commit is contained in:
Andreas Kling 2019-03-05 10:34:08 +01:00
parent b67d0a3632
commit 251293f2e1
Notes: sideshowbarker 2024-07-19 15:33:07 +09:00
6 changed files with 110 additions and 69 deletions

View file

@ -51,6 +51,8 @@ void BXVGADevice::set_register(word index, word data)
void BXVGADevice::set_resolution(int width, int height)
{
m_framebuffer_size = { width, height };
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
set_register(VBE_DISPI_INDEX_XRES, (word)width);
set_register(VBE_DISPI_INDEX_YRES, (word)height);

View file

@ -374,7 +374,7 @@ int Process::do_exec(String path, Vector<String> arguments, Vector<String> envir
m_signal_stack_user_region = nullptr;
m_display_framebuffer_region = nullptr;
set_default_signal_dispositions();
m_signal_mask = 0xffffffff;
m_signal_mask = 0;
m_pending_signals = 0;
for (int i = 0; i < m_fds.size(); ++i) {
@ -778,13 +778,13 @@ void Process::send_signal(byte signal, Process* sender)
bool Process::has_unmasked_pending_signals() const
{
return m_pending_signals & m_signal_mask;
return m_pending_signals & ~m_signal_mask;
}
ShouldUnblockProcess Process::dispatch_one_pending_signal()
{
ASSERT_INTERRUPTS_DISABLED();
dword signal_candidates = m_pending_signals & m_signal_mask;
dword signal_candidates = m_pending_signals & ~m_signal_mask;
ASSERT(signal_candidates);
byte signal = 0;
@ -854,7 +854,9 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
ASSERT_INTERRUPTS_DISABLED();
ASSERT(signal < 32);
dbgprintf("dispatch_signal %s(%u) <- %u\n", name().characters(), pid(), signal);
#ifdef SIGNAL_DEBUG
kprintf("dispatch_signal %s(%u) <- %u\n", name().characters(), pid(), signal);
#endif
auto& action = m_signal_action_data[signal];
// FIXME: Implement SA_SIGINFO signal handlers.
@ -890,10 +892,21 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
}
if (handler_laddr.as_ptr() == SIG_IGN) {
dbgprintf("%s(%u) ignored signal %u\n", name().characters(), pid(), signal);
#ifdef SIGNAL_DEBUG
kprintf("%s(%u) ignored signal %u\n", name().characters(), pid(), signal);
#endif
return ShouldUnblockProcess::Yes;
}
dword old_signal_mask = m_signal_mask;
dword new_signal_mask = action.mask;
if (action.flags & SA_NODEFER)
new_signal_mask &= ~(1 << signal);
else
new_signal_mask |= 1 << signal;
m_signal_mask |= new_signal_mask;
Scheduler::prepare_to_modify_tss(*this);
word ret_cs = m_tss.cs;
@ -902,11 +915,13 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
bool interrupting_in_kernel = (ret_cs & 3) == 0;
if (interrupting_in_kernel) {
dbgprintf("dispatch_signal to %s(%u) in state=%s with return to %w:%x\n", name().characters(), pid(), to_string(state()), ret_cs, ret_eip);
#ifdef SIGNAL_DEBUG
kprintf("dispatch_signal to %s(%u) in state=%s with return to %w:%x\n", name().characters(), pid(), to_string(state()), ret_cs, ret_eip);
#endif
ASSERT(is_blocked());
m_tss_to_resume_kernel = m_tss;
#ifdef SIGNAL_DEBUG
dbgprintf("resume tss pc: %w:%x\n", m_tss_to_resume_kernel.cs, m_tss_to_resume_kernel.eip);
kprintf("resume tss pc: %w:%x\n", m_tss_to_resume_kernel.cs, m_tss_to_resume_kernel.eip);
#endif
}
@ -917,33 +932,31 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
m_signal_stack_user_region = allocate_region(LinearAddress(), default_userspace_stack_size, "signal stack (user)");
ASSERT(m_signal_stack_user_region);
m_signal_stack_kernel_region = allocate_region(LinearAddress(), default_userspace_stack_size, "signal stack (kernel)");
ASSERT(m_signal_stack_user_region);
ASSERT(m_signal_stack_kernel_region);
}
m_tss.ss = 0x23;
m_tss.esp = m_signal_stack_user_region->laddr().offset(default_userspace_stack_size).get() & 0xfffffff8;
m_tss.esp = m_signal_stack_user_region->laddr().offset(default_userspace_stack_size).get();
m_tss.ss0 = 0x10;
m_tss.esp0 = m_signal_stack_kernel_region->laddr().offset(default_userspace_stack_size).get() & 0xfffffff8;
push_value_on_stack(ret_eflags);
push_value_on_stack(ret_cs);
push_value_on_stack(ret_eip);
m_tss.esp0 = m_signal_stack_kernel_region->laddr().offset(default_userspace_stack_size).get();
} else {
push_value_on_stack(ret_cs);
push_value_on_stack(ret_eip);
push_value_on_stack(ret_eflags);
// PUSHA
dword old_esp = m_tss.esp;
push_value_on_stack(m_tss.eax);
push_value_on_stack(m_tss.ecx);
push_value_on_stack(m_tss.edx);
push_value_on_stack(m_tss.ebx);
push_value_on_stack(old_esp);
push_value_on_stack(m_tss.ebp);
push_value_on_stack(m_tss.esi);
push_value_on_stack(m_tss.edi);
}
// PUSHA
dword old_esp = m_tss.esp;
push_value_on_stack(m_tss.eax);
push_value_on_stack(m_tss.ecx);
push_value_on_stack(m_tss.edx);
push_value_on_stack(m_tss.ebx);
push_value_on_stack(old_esp);
push_value_on_stack(m_tss.ebp);
push_value_on_stack(m_tss.esi);
push_value_on_stack(m_tss.edi);
// PUSH old_signal_mask
push_value_on_stack(old_signal_mask);
m_tss.eax = (dword)signal;
m_tss.cs = 0x1b;
m_tss.ds = 0x23;
m_tss.es = 0x23;
@ -957,6 +970,12 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
auto* region = allocate_region(LinearAddress(), PAGE_SIZE, "signal_trampoline", true, true);
m_return_to_ring3_from_signal_trampoline = region->laddr();
byte* code_ptr = m_return_to_ring3_from_signal_trampoline.as_ptr();
*code_ptr++ = 0x5a; // pop edx
*code_ptr++ = 0xb8; // mov eax, <dword>
*(dword*)code_ptr = Syscall::SC_restore_signal_mask;
code_ptr += sizeof(dword);
*code_ptr++ = 0xcd; // int 0x82
*code_ptr++ = 0x82;
*code_ptr++ = 0x61; // popa
*code_ptr++ = 0x9d; // popf
*code_ptr++ = 0xc3; // ret
@ -964,7 +983,12 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
*code_ptr++ = 0x0b;
m_return_to_ring0_from_signal_trampoline = LinearAddress((dword)code_ptr);
*code_ptr++ = 0x61; // popa
*code_ptr++ = 0x5a; // pop edx
*code_ptr++ = 0xb8; // mov eax, <dword>
*(dword*)code_ptr = Syscall::SC_restore_signal_mask;
code_ptr += sizeof(dword);
*code_ptr++ = 0xcd; // int 0x82
*code_ptr++ = 0x82;
*code_ptr++ = 0xb8; // mov eax, <dword>
*(dword*)code_ptr = Syscall::SC_sigreturn;
code_ptr += sizeof(dword);
@ -972,12 +996,11 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
*code_ptr++ = 0x82;
*code_ptr++ = 0x0f; // ud2
*code_ptr++ = 0x0b;
// FIXME: For !SA_NODEFER, maybe we could do something like emitting an int 0x80 syscall here that
// unmasks the signal so it can be received again? I guess then I would need one trampoline
// per signal number if it's hard-coded, but it's just a few bytes per each.
}
// FIXME: Should we worry about the stack being 16 byte aligned when entering a signal handler?
push_value_on_stack(signal);
if (interrupting_in_kernel)
push_value_on_stack(m_return_to_ring0_from_signal_trampoline.get());
else
@ -987,19 +1010,25 @@ ShouldUnblockProcess Process::dispatch_signal(byte signal)
set_state(Skip1SchedulerPass);
#ifdef SIGNAL_DEBUG
dbgprintf("signal: Okay, %s(%u) {%s} has been primed with signal handler %w:%x\n", name().characters(), pid(), to_string(state()), m_tss.cs, m_tss.eip);
kprintf("signal: Okay, %s(%u) {%s} has been primed with signal handler %w:%x\n", name().characters(), pid(), to_string(state()), m_tss.cs, m_tss.eip);
#endif
return ShouldUnblockProcess::Yes;
}
int Process::sys$restore_signal_mask(dword mask)
{
m_signal_mask = mask;
return 0;
}
void Process::sys$sigreturn()
{
InterruptDisabler disabler;
Scheduler::prepare_to_modify_tss(*this);
m_tss = m_tss_to_resume_kernel;
#ifdef SIGNAL_DEBUG
dbgprintf("sys$sigreturn in %s(%u)\n", name().characters(), pid());
dbgprintf(" -> resuming execution at %w:%x\n", m_tss.cs, m_tss.eip);
kprintf("sys$sigreturn in %s(%u)\n", name().characters(), pid());
kprintf(" -> resuming execution at %w:%x\n", m_tss.cs, m_tss.eip);
#endif
set_state(Skip1SchedulerPass);
Scheduler::yield();
@ -2638,3 +2667,41 @@ void* Process::sys$get_shared_buffer(int shared_buffer_id)
#endif
return shared_buffer.retain(*this);
}
const char* to_string(Process::State state)
{
switch (state) {
case Process::Invalid: return "Invalid";
case Process::Runnable: return "Runnable";
case Process::Running: return "Running";
case Process::Dying: return "Dying";
case Process::Dead: return "Dead";
case Process::Stopped: return "Stopped";
case Process::Skip1SchedulerPass: return "Skip1";
case Process::Skip0SchedulerPasses: return "Skip0";
case Process::BlockedSleep: return "Sleep";
case Process::BlockedWait: return "Wait";
case Process::BlockedRead: return "Read";
case Process::BlockedWrite: return "Write";
case Process::BlockedSignal: return "Signal";
case Process::BlockedSelect: return "Select";
case Process::BlockedLurking: return "Lurking";
case Process::BlockedConnect: return "Connect";
case Process::BeingInspected: return "Inspect";
}
kprintf("to_string(Process::State): Invalid state: %u\n", state);
ASSERT_NOT_REACHED();
return nullptr;
}
const char* to_string(Process::Priority priority)
{
switch (priority) {
case Process::LowPriority: return "Low";
case Process::NormalPriority: return "Normal";
case Process::HighPriority: return "High";
}
kprintf("to_string(Process::Priority): Invalid priority: %u\n", priority);
ASSERT_NOT_REACHED();
return nullptr;
}

View file

@ -227,6 +227,7 @@ public:
int sys$listen(int sockfd, int backlog);
int sys$accept(int sockfd, sockaddr*, socklen_t*);
int sys$connect(int sockfd, const sockaddr*, socklen_t);
int sys$restore_signal_mask(dword mask);
int sys$create_shared_buffer(pid_t peer_pid, size_t, void** buffer);
void* sys$get_shared_buffer(int shared_buffer_id);
@ -365,7 +366,7 @@ private:
size_t m_max_open_file_descriptors { 16 };
SignalActionData m_signal_action_data[32];
dword m_pending_signals { 0 };
dword m_signal_mask { 0xffffffff };
dword m_signal_mask { 0 };
RetainPtr<Socket> m_blocked_connecting_socket;
byte m_termination_status { 0 };
@ -439,41 +440,8 @@ private:
Process::State m_original_state { Process::Invalid };
};
static inline const char* to_string(Process::State state)
{
switch (state) {
case Process::Invalid: return "Invalid";
case Process::Runnable: return "Runnable";
case Process::Running: return "Running";
case Process::Dying: return "Dying";
case Process::Dead: return "Dead";
case Process::Stopped: return "Stopped";
case Process::Skip1SchedulerPass: return "Skip1";
case Process::Skip0SchedulerPasses: return "Skip0";
case Process::BlockedSleep: return "Sleep";
case Process::BlockedWait: return "Wait";
case Process::BlockedRead: return "Read";
case Process::BlockedWrite: return "Write";
case Process::BlockedSignal: return "Signal";
case Process::BlockedSelect: return "Select";
case Process::BlockedLurking: return "Lurking";
case Process::BlockedConnect: return "Connect";
case Process::BeingInspected: return "Inspect";
}
ASSERT_NOT_REACHED();
return nullptr;
}
static inline const char* to_string(Process::Priority state)
{
switch (state) {
case Process::LowPriority: return "Low";
case Process::NormalPriority: return "Normal";
case Process::HighPriority: return "High";
}
ASSERT_NOT_REACHED();
return nullptr;
}
extern const char* to_string(Process::State);
extern const char* to_string(Process::Priority);
extern void block(Process::State);
extern void sleep(dword ticks);

View file

@ -221,6 +221,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2,
return current->sys$release_shared_buffer((int)arg1);
case Syscall::SC_chown:
return current->sys$chown((const char*)arg1, (uid_t)arg2, (gid_t)arg3);
case Syscall::SC_restore_signal_mask:
return current->sys$restore_signal_mask((dword)arg1);
default:
kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3);
break;

View file

@ -85,6 +85,7 @@
__ENUMERATE_SYSCALL(chown) \
__ENUMERATE_SYSCALL(fchmod) \
__ENUMERATE_SYSCALL(symlink) \
__ENUMERATE_SYSCALL(restore_signal_mask) \
namespace Syscall {

View file

@ -249,6 +249,7 @@ struct sigaction {
#define SA_NOCLDSTOP 1
#define SA_NOCLDWAIT 2
#define SA_SIGINFO 4
#define SA_NODEFER 0x40000000
#define SIG_BLOCK 0
#define SIG_UNBLOCK 1