mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 08:32:43 +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:
		
							parent
							
								
									52d502e11f
								
							
						
					
					
						commit
						153ea704af
					
				
					 13 changed files with 240 additions and 30 deletions
				
			
		|  | @ -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(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -56,6 +56,7 @@ enum Function { | |||
|     Getdtablesize = 0x2024, | ||||
|     Dup = 0x2025, | ||||
|     Dup2 = 0x2026, | ||||
|     Sigaction = 0x2027, | ||||
| }; | ||||
| 
 | ||||
| void initialize(); | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,8 +11,10 @@ typedef uint32_t 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); | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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" { | ||||
|  |  | |||
|  | @ -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) | ||||
| { | ||||
|  |  | |||
|  | @ -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 PID\n", argv[1]); | ||||
|             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[pid_argi]); | ||||
|         return 3; | ||||
|     } | ||||
| 
 | ||||
|     kill((pid_t)value, SIGKILL); | ||||
|     kill((pid_t)pid, signum); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling