1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 11:57:34 +00:00

Add some basic signal support.

It only works for sending a signal to a process that's in userspace code.

We implement reception by synthesizing a PUSHA+PUSHF in the receiving process
(operating on values in the TSS.)
The TSS CS:EIP is then rerouted to the signal handler and a tiny return
trampoline is constructed in a dedicated region in the receiving process.

Also hacked up /bin/kill to be able to send arbitrary signals (kill -N PID)
This commit is contained in:
Andreas Kling 2018-11-06 10:46:40 +01:00
parent 52d502e11f
commit 153ea704af
13 changed files with 240 additions and 30 deletions

View file

@ -218,6 +218,8 @@ Region* MemoryManager::region_from_laddr(Process& process, LinearAddress laddr)
if (region->contains(laddr))
return region.ptr();
}
kprintf("%s(%u) Couldn't find region for L%x\n", process.name().characters(), process.pid(), laddr.get());
process.dumpRegions();
ASSERT_NOT_REACHED();
}

View file

@ -731,17 +731,18 @@ void Process::sys$exit(int status)
switchNow();
}
void Process::send_signal(int signal, Process* sender)
void Process::terminate_due_to_signal(int signal, Process* sender)
{
ASSERT_INTERRUPTS_DISABLED();
bool wasCurrent = current == sender;
bool wasCurrent = this == current;
set_state(Exiting);
s_processes->remove(this);
notify_waiters(m_pid, 0, signal);
if (wasCurrent) {
kprintf("Current process committing suicide!\n");
kprintf("Current process (%u) committing suicide!\n", pid());
if (!scheduleNewProcess()) {
kprintf("Process::send_signal: Failed to schedule a new process :(\n");
HANG;
@ -752,6 +753,75 @@ void Process::send_signal(int signal, Process* sender)
switchNow();
}
void Process::send_signal(int signal, Process* sender)
{
ASSERT_INTERRUPTS_DISABLED();
ASSERT(signal < 32);
// FIXME: Handle send_signal to self.
ASSERT(this != current);
auto& action = m_signal_action_data[signal];
// FIXME: Implement SA_SIGINFO signal handlers.
ASSERT(!(action.flags & SA_SIGINFO));
auto handler_laddr = action.handler_or_sigaction;
if (handler_laddr.is_null())
return terminate_due_to_signal(signal, sender);
word ret_cs = m_tss.cs;
dword ret_eip = m_tss.eip;
dword ret_eflags = m_tss.eflags;
if ((ret_cs & 3) == 0) {
// FIXME: Handle send_signal to process currently in kernel code.
ASSERT_NOT_REACHED();
}
ProcessPagingScope pagingScope(*this);
dword old_esp = m_tss.esp;
push_value_on_stack(ret_eip);
push_value_on_stack(ret_eflags);
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);
m_tss.eax = (dword)signal;
m_tss.cs = 0x1b;
m_tss.eip = handler_laddr.get();
if (m_return_from_signal_trampoline.is_null()) {
auto* region = allocate_region(LinearAddress(), PAGE_SIZE, "signal_trampoline", true, true); // FIXME: Remap as read-only after setup.
m_return_from_signal_trampoline = region->linearAddress;
byte* code_ptr = m_return_from_signal_trampoline.asPtr();
*code_ptr++ = 0x61; // popa
*code_ptr++ = 0x9d; // popf
*code_ptr++ = 0xc3; // ret
*code_ptr++ = 0x0f; // ud2
*code_ptr++ = 0x0b;
}
push_value_on_stack(m_return_from_signal_trampoline.get());
dbgprintf("signal: %s(%u) sent %d to %s(%u)\n", sender->name().characters(), sender->pid(), signal, name().characters(), pid());
if (sender == this) {
yield();
ASSERT_NOT_REACHED();
}
}
void Process::push_value_on_stack(dword value)
{
m_tss.esp -= 4;
dword* stack_ptr = (dword*)m_tss.esp;
*stack_ptr = value;
}
void Process::processDidCrash(Process* crashedProcess)
{
ASSERT_INTERRUPTS_DISABLED();
@ -1183,12 +1253,6 @@ int Process::sys$isatty(int fd)
return 1;
}
Unix::sighandler_t Process::sys$signal(int signum, Unix::sighandler_t handler)
{
dbgprintf("sys$signal: %d => L%x\n", signum, handler);
return nullptr;
}
int Process::sys$kill(pid_t pid, int signal)
{
if (pid == 0) {
@ -1466,3 +1530,26 @@ int Process::sys$dup2(int old_fd, int new_fd)
m_file_descriptors[new_fd] = handle;
return new_fd;
}
Unix::sighandler_t Process::sys$signal(int signum, Unix::sighandler_t handler)
{
// FIXME: Fail with -EINVAL if attepmting to catch or ignore SIGKILL or SIGSTOP.
if (signum >= 32)
return (Unix::sighandler_t)-EINVAL;
dbgprintf("sys$signal: %d => L%x\n", signum, handler);
return nullptr;
}
int Process::sys$sigaction(int signum, const Unix::sigaction* act, Unix::sigaction* old_act)
{
// FIXME: Fail with -EINVAL if attepmting to change action for SIGKILL or SIGSTOP.
if (signum >= 32)
return -EINVAL;
VALIDATE_USER_READ(act, sizeof(Unix::sigaction));
InterruptDisabler disabler; // FIXME: This should use a narrower lock.
auto& action = m_signal_action_data[signum];
action.restorer = LinearAddress((dword)act->sa_restorer);
action.flags = act->sa_flags;
action.handler_or_sigaction = LinearAddress((dword)act->sa_sigaction);
return 0;
}

View file

@ -15,6 +15,13 @@ class PageDirectory;
class Region;
class Zone;
struct SignalActionData {
LinearAddress handler_or_sigaction;
dword mask { 0 };
int flags { 0 };
LinearAddress restorer;
};
class Process : public InlineLinkedListNode<Process> {
friend class InlineLinkedListNode<Process>;
public:
@ -129,6 +136,7 @@ public:
int sys$getdtablesize();
int sys$dup(int oldfd);
int sys$dup2(int oldfd, int newfd);
int sys$sigaction(int signum, const Unix::sigaction* act, Unix::sigaction* old_act);
static void initialize();
@ -162,6 +170,7 @@ public:
const FileHandle* file_descriptor(size_t i) const { return m_file_descriptors[i].ptr(); }
void send_signal(int signal, Process* sender);
void terminate_due_to_signal(int signal, Process* sender);
Process* fork(RegisterDump&);
int exec(const String& path, Vector<String>&& arguments, Vector<String>&& environment);
@ -172,7 +181,7 @@ private:
Process(String&& name, uid_t, gid_t, pid_t parentPID, RingLevel, RetainPtr<VirtualFileSystem::Node>&& cwd = nullptr, RetainPtr<VirtualFileSystem::Node>&& executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr);
void allocateLDT();
void push_value_on_stack(dword);
PageDirectory* m_page_directory { nullptr };
@ -205,6 +214,7 @@ private:
int m_waiteeStatus { 0 };
int m_fdBlockedOnRead { -1 };
size_t m_max_open_file_descriptors { 16 };
SignalActionData m_signal_action_data[32];
RetainPtr<VirtualFileSystem::Node> m_cwd;
RetainPtr<VirtualFileSystem::Node> m_executable;
@ -221,6 +231,8 @@ private:
// FIXME: Implement some kind of ASLR?
LinearAddress m_nextRegion;
LinearAddress m_return_from_signal_trampoline;
pid_t m_parentPID { 0 };
static void notify_waiters(pid_t waitee, int exit_status, int signal);

View file

@ -146,6 +146,8 @@ static DWORD handle(RegisterDump& regs, DWORD function, DWORD arg1, DWORD arg2,
return current->sys$dup((int)arg1);
case Syscall::Dup2:
return current->sys$dup2((int)arg1, (int)arg2);
case Syscall::Sigaction:
return current->sys$sigaction((int)arg1, (const Unix::sigaction*)arg2, (Unix::sigaction*)arg3);
default:
kprintf("<%u> int0x80: Unknown function %x requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3);
break;

View file

@ -56,6 +56,7 @@ enum Function {
Getdtablesize = 0x2024,
Dup = 0x2025,
Dup2 = 0x2026,
Sigaction = 0x2027,
};
void initialize();