Ver Fonte

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)
Andreas Kling há 6 anos atrás
pai
commit
153ea704af

+ 2 - 0
Kernel/MemoryManager.cpp

@@ -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();
 }
 

+ 96 - 9
Kernel/Process.cpp

@@ -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;
+}

+ 13 - 1
Kernel/Process.h

@@ -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);

+ 2 - 0
Kernel/Syscall.cpp

@@ -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;

+ 1 - 0
Kernel/Syscall.h

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

+ 6 - 0
LibC/signal.cpp

@@ -22,5 +22,11 @@ sighandler_t signal(int signum, sighandler_t handler)
     return old_handler;
 }
 
+int sigaction(int signum, const struct sigaction* act, struct sigaction* old_act)
+{
+    int rc = Syscall::invoke(Syscall::Sigaction, (dword)signum, (dword)act, (dword)old_act);
+    __RETURN_WITH_ERRNO(rc, rc, -1);
+}
+
 }
 

+ 9 - 2
LibC/signal.h

@@ -11,8 +11,10 @@ typedef uint32_t sigset_t;
 typedef void siginfo_t;
 
 struct sigaction {
-    void (*sa_handler)(int);
-    void (*sa_sigaction)(int, siginfo_t*, void*);
+    union {
+        void (*sa_handler)(int);
+        void (*sa_sigaction)(int, siginfo_t*, void*);
+    };
     sigset_t sa_mask;
     int sa_flags;
     void (*sa_restorer)(void);
@@ -20,11 +22,16 @@ struct sigaction {
 
 int kill(pid_t, int sig);
 sighandler_t signal(int sig, sighandler_t);
+int sigaction(int signum, const struct sigaction* act, struct sigaction* old_act);
 
 #define SIG_DFL ((__sighandler_t)0)
 #define SIG_ERR ((__sighandler_t)-1)
 #define SIG_IGN ((__sighandler_t)1)
 
+#define SA_NOCLDSTOP 1
+#define SA_NOCLDWAIT 2
+#define SA_SIGINFO 4
+
 #define SIG_BLOCK 0
 #define SIG_UNBLOCK 1
 #define SIG_SETMASK 2

+ 3 - 3
LibC/stdlib.h

@@ -5,15 +5,15 @@
 
 __BEGIN_DECLS
 
-void* malloc(size_t);
+void* malloc(size_t) __MALLOC;
 void free(void*);
 void* calloc(size_t nmemb, size_t);
 void* realloc(void *ptr, size_t);
 char* getenv(const char* name);
 int atoi(const char*);
 
-void exit(int status);
-void abort();
+void exit(int status) __NORETURN;
+void abort() __NORETURN;
 
 __END_DECLS
 

+ 1 - 0
LibC/sys/cdefs.h

@@ -4,6 +4,7 @@
 
 #define ALWAYS_INLINE inline __attribute__ ((always_inline))
 #define __NORETURN __attribute__ ((noreturn))
+#define __MALLOC __attribute__ ((malloc))
 
 #ifdef __cplusplus
 #define __BEGIN_DECLS extern "C" {

+ 6 - 4
Userland/cat.cpp

@@ -1,7 +1,9 @@
-#include <LibC/stdio.h>
-#include <LibC/unistd.h>
-#include <LibC/errno.h>
-#include <LibC/string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
 
 int main(int argc, char** argv)
 {

+ 28 - 11
Userland/kill.cpp

@@ -1,6 +1,7 @@
-#include <LibC/unistd.h>
-#include <LibC/stdio.h>
-#include <LibC/signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
 #include <AK/String.h>
 
 static unsigned parseUInt(const String& str, bool& ok)
@@ -18,20 +19,36 @@ static unsigned parseUInt(const String& str, bool& ok)
     return value;
 }
 
+static void print_usage_and_exit()
+{
+    printf("usage: kill [-signal] <PID>\n");
+    exit(1);
+}
+
 int main(int argc, char** argv)
 {
-    if (argc < 2) {
-        printf("usage: kill <PID>\n");
-        return 1;
-    }
+    if (argc != 2 && argc != 3)
+        print_usage_and_exit();
     bool ok;
-    unsigned value = parseUInt(argv[1], ok);
+    unsigned signum = SIGTERM;
+    int pid_argi = 1;
+    if (argc == 3) {
+        pid_argi = 2;
+        if (argv[1][0] != '-')
+            print_usage_and_exit();
+        signum = parseUInt(&argv[1][1], ok);
+        if (!ok) {
+            printf("%s is not a valid signal number\n", &argv[1][1]);
+            return 2;
+        }
+    }
+    unsigned pid = parseUInt(argv[pid_argi], ok);
     if (!ok) {
-        printf("%s is not a valid PID\n", argv[1]);
-        return 2;
+        printf("%s is not a valid PID\n", argv[pid_argi]);
+        return 3;
     }
 
-    kill((pid_t)value, SIGKILL);
+    kill((pid_t)pid, signum);
     return 0;
 }
 

+ 28 - 0
Userland/sh.cpp

@@ -6,6 +6,7 @@
 #include <LibC/stdlib.h>
 #include <LibC/utsname.h>
 #include <LibC/pwd.h>
+#include <signal.h>
 #include <AK/FileSystemPath.h>
 
 struct GlobalState {
@@ -32,6 +33,29 @@ static int sh_pwd(int, const char**)
     return 0;
 }
 
+void did_receive_signal(int signum)
+{
+    printf("\nMy word, I've received a signal with number %d\n", signum);
+    //exit(0);
+}
+
+static int sh_busy(int, const char**)
+{
+    struct sigaction sa;
+    sa.sa_handler = did_receive_signal;
+    sa.sa_flags = 0;
+    sa.sa_mask = 0;
+    sa.sa_restorer = nullptr;
+    int rc = sigaction(SIGUSR1, &sa, nullptr);
+    assert(rc == 0);
+    printf("listening for SIGUSR1 while looping in userspace...\n");
+    for (;;) {
+        for (volatile int i = 0; i < 100000; ++i)
+            ;
+    }
+    return 0;
+}
+
 static int sh_fork(int, const char**)
 {
     pid_t pid = fork();
@@ -147,6 +171,10 @@ static bool handle_builtin(int argc, const char** argv, int& retval)
         retval = sh_fef(argc, argv);
         return true;
     }
+    if (!strcmp(argv[0], "busy")) {
+        retval = sh_busy(argc, argv);
+        return true;
+    }
     if (!strcmp(argv[0], "wt")) {
         retval = sh_wt(argc, argv);
         return true;

+ 45 - 0
VirtualFileSystem/UnixTypes.h

@@ -20,9 +20,54 @@ typedef dword nlink_t;
 typedef dword uid_t;
 typedef dword gid_t;
 
+#ifdef SERENITY
 typedef void (*__sighandler_t)(int);
 typedef __sighandler_t sighandler_t;
 
+typedef dword sigset_t;
+typedef void siginfo_t;
+
+struct sigaction {
+    union {
+        void (*sa_handler)(int);
+        void (*sa_sigaction)(int, siginfo_t*, void*);
+    };
+    sigset_t sa_mask;
+    int sa_flags;
+    void (*sa_restorer)(void);
+};
+
+#define SA_NOCLDSTOP 1
+#define SA_NOCLDWAIT 2
+#define SA_SIGINFO 4
+
+#define SIG_BLOCK 0
+#define SIG_UNBLOCK 1
+#define SIG_SETMASK 2
+
+#define SIGHUP    1
+#define SIGINT    2
+#define SIGQUIT   3
+#define SIGILL    4
+#define SIGTRAP   5
+#define SIGABRT   6
+#define SIGBUS    7
+#define SIGFPE    8
+#define SIGKILL   9
+#define SIGUSR1  10
+#define SIGSEGV  11
+#define SIGUSR2  12
+#define SIGPIPE  13
+#define SIGALRM  14
+#define SIGTERM  15
+#define SIGCONT  18
+#define SIGTSTP  20
+#define SIGTTIN  21
+#define SIGTTOU  22
+
+#endif
+
+
 #ifdef SERENITY
 // FIXME: Support 64-bit offsets!
 typedef signed_dword off_t;