From 20c1f1d0d4fcbc2db5185954a846fc84df2f0dd8 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 20 Oct 2018 12:57:31 +0200 Subject: [PATCH] Add signal mask support to ppoll(2). --- kernel/include/sortix/kernel/signal.h | 3 +- kernel/include/sortix/kernel/thread.h | 4 +- kernel/poll.cpp | 71 +++++++++++++++++++++--- kernel/signal.cpp | 78 ++++++++++++++++++++------- kernel/thread.cpp | 4 +- 5 files changed, 131 insertions(+), 29 deletions(-) diff --git a/kernel/include/sortix/kernel/signal.h b/kernel/include/sortix/kernel/signal.h index 447da683..0dc4dceb 100644 --- a/kernel/include/sortix/kernel/signal.h +++ b/kernel/include/sortix/kernel/signal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2013, 2014, 2018 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -34,6 +34,7 @@ namespace Signal { void Init(); inline bool IsPending() { return asm_signal_is_pending != 0; } +void UpdateMask(int how, const sigset_t* set, sigset_t* oldset); void DispatchHandler(struct interrupt_context* intctx, void* user); void ReturnHandler(struct interrupt_context* intctx, void* user); diff --git a/kernel/include/sortix/kernel/thread.h b/kernel/include/sortix/kernel/thread.h index 8cfcf4af..ca44500d 100644 --- a/kernel/include/sortix/kernel/thread.h +++ b/kernel/include/sortix/kernel/thread.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2018 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -73,6 +73,7 @@ public: volatile ThreadState state; sigset_t signal_pending; sigset_t signal_mask; + sigset_t saved_signal_mask; stack_t signal_stack; addr_t kernelstackpos; size_t kernelstacksize; @@ -83,6 +84,7 @@ public: bool pledged_destruction; bool force_no_signals; bool signal_single; + bool has_saved_signal_mask; Clock execute_clock; Clock system_clock; diff --git a/kernel/poll.cpp b/kernel/poll.cpp index 10aa1b8c..3f476c24 100644 --- a/kernel/poll.cpp +++ b/kernel/poll.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, 2015 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2014, 2015, 2018 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -38,7 +39,9 @@ #include #include #include +#include #include +#include #include #include @@ -202,14 +205,42 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, if ( !FetchTimespec(&timeout_ts, user_timeout_ts) ) return -1; + sigset_t oldsigmask; if ( user_sigmask ) - return errno = ENOSYS, -1; + { + sigset_t sigmask; + if ( !CopyFromUser(&sigmask, user_sigmask, sizeof(sigset_t)) ) + return -1; + Signal::UpdateMask(SIG_SETMASK, &sigmask, &oldsigmask); + if ( Signal::IsPending() ) + { + // The pending signal might only be pending with the temporary + // signal mask, so don't restore it. Instead ask for the real signal + // mask to be restored after the signal has been processed. + Thread* thread = CurrentThread(); + thread->has_saved_signal_mask = true; + memcpy(&thread->saved_signal_mask, &oldsigmask, sizeof(sigset_t)); + assert(Signal::IsPending()); + return errno = EINTR, -1; + } + } struct pollfd* fds = CopyFdsFromUser(user_fds, nfds); - if ( !fds ) { return -1; } + if ( !fds ) + { + if ( user_sigmask ) + Signal::UpdateMask(SIG_SETMASK, &oldsigmask, NULL); + return -1; + } PollNode* nodes = new PollNode[nfds]; - if ( !nodes ) { delete[] fds; return -1; } + if ( !nodes ) + { + delete[] fds; + if ( user_sigmask ) + Signal::UpdateMask(SIG_SETMASK, &oldsigmask, NULL); + return -1; + } Process* process = CurrentProcess(); @@ -222,6 +253,7 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, bool self_woken = false; bool remote_woken = false; bool unexpected_error = false; + bool deliver_signal = false; Timer timer; struct poll_timeout pts; @@ -253,7 +285,11 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, continue; } Ref desc = process->GetDescriptor(fds[reqs].fd); - if ( !desc ) { self_woken = unexpected_error = true; break; } + if ( !desc ) + { + self_woken = unexpected_error = true; + break; + } node->events = fds[reqs].events | POLL__ONLY_REVENTS; node->revents = 0; node->wake_mutex = &wakeup_mutex; @@ -275,8 +311,11 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, while ( !(self_woken || remote_woken) ) { if ( !kthread_cond_wait_signal(&wakeup_cond, &wakeup_mutex) ) - errno = -EINTR, + { + errno = -EINTR; self_woken = true; + deliver_signal = true; + } } kthread_mutex_unlock(&wakeup_mutex); @@ -308,6 +347,26 @@ int sys_ppoll(struct pollfd* user_fds, size_t nfds, delete[] nodes; delete[] fds; + + if ( 0 <= ret ) + errno = 0; + + if ( user_sigmask ) + { + if ( !deliver_signal ) + Signal::UpdateMask(SIG_SETMASK, &oldsigmask, NULL); + else + { + // The pending signal might only be pending with the temporary + // signal mask, so don't restore it. Instead ask for the real signal + // mask to be restored after the signal has been processed. + assert(Signal::IsPending()); + Thread* thread = CurrentThread(); + thread->has_saved_signal_mask = true; + memcpy(&thread->saved_signal_mask, &oldsigmask, sizeof(sigset_t)); + } + } + return ret; } diff --git a/kernel/signal.cpp b/kernel/signal.cpp index 010d8bf3..db50bafa 100644 --- a/kernel/signal.cpp +++ b/kernel/signal.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2018 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -189,7 +189,9 @@ int sys_sigpending(sigset_t* set) return CopyToUser(set, &thread->signal_pending, sizeof(sigset_t)) ? 0 : -1; } -int sys_sigprocmask(int how, const sigset_t* user_set, sigset_t* user_oldset) +namespace Signal { + +void UpdateMask(int how, const sigset_t* set, sigset_t* oldset) { Process* process = CurrentProcess(); Thread* thread = CurrentThread(); @@ -199,38 +201,46 @@ int sys_sigprocmask(int how, const sigset_t* user_set, sigset_t* user_oldset) ScopedLock lock(&process->signal_lock); // Let the caller know the previous signal mask. - if ( user_oldset ) - { - if ( !CopyToUser(user_oldset, &thread->signal_mask, sizeof(sigset_t)) ) - return -1; - } + if ( oldset ) + memcpy(oldset, &thread->signal_mask, sizeof(sigset_t)); // Update the current signal mask according to how. - if ( user_set ) + if ( set ) { - sigset_t set; - if ( !CopyFromUser(&set, user_set, sizeof(sigset_t)) ) - return -1; - switch ( how ) { case SIG_BLOCK: - sigorset(&thread->signal_mask, &thread->signal_mask, &set); + sigorset(&thread->signal_mask, &thread->signal_mask, set); break; case SIG_UNBLOCK: - signotset(&set, &set); - sigandset(&thread->signal_mask, &thread->signal_mask, &set); + { + sigset_t notset; + signotset(¬set, set); + sigandset(&thread->signal_mask, &thread->signal_mask, ¬set); break; + } case SIG_SETMASK: - memcpy(&thread->signal_mask, &set, sizeof(sigset_t)); + memcpy(&thread->signal_mask, set, sizeof(sigset_t)); break; - default: - return errno = EINVAL, -1; }; UpdatePendingSignals(thread); } +} +} // namespace Signal + +int sys_sigprocmask(int how, const sigset_t* user_set, sigset_t* user_oldset) +{ + if ( how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK ) + return errno = EINVAL, -1; + sigset_t set, oldset; + if ( user_set && !CopyFromUser(&set, user_set, sizeof(sigset_t)) ) + return -1; + Signal::UpdateMask( + how, user_set ? &set : NULL, user_oldset ? &oldset : NULL); + if ( user_oldset && !CopyToUser(user_oldset, &oldset, sizeof(sigset_t)) ) + return -1; return 0; } @@ -563,7 +573,16 @@ retry_another_signal: // Decide which signal to deliver to the thread. int signum = PickImportantSignal(&deliverable_signals); if ( !signum ) + { + if ( has_saved_signal_mask ) + { + memcpy(&signal_mask, &saved_signal_mask, sizeof(sigset_t)); + has_saved_signal_mask = false; + UpdatePendingSignals(this); + goto retry_another_signal; + } return; + } // Unmark the selected signal as pending. sigdelset(&signal_pending, signum); @@ -764,7 +783,20 @@ retry_another_signal: // Format the ucontext into the stack frame. stack_frame.ucontext.uc_link = NULL; - memcpy(&stack_frame.ucontext.uc_sigmask, &signal_mask, sizeof(signal_mask)); + if ( has_saved_signal_mask ) + { + // If a system call temporarily set another signal mask, it wants us to + // deliver signals for that temporary signal masks, however we must + // restore the original saved signal mask in that case. + memcpy(&stack_frame.ucontext.uc_sigmask, &saved_signal_mask, + sizeof(saved_signal_mask)); + // 'has_saved_signal_mask' is set to false below when it's actually + // delivered. This handles the case where the signal delivery fails and + // and instead turns into another (we still want to restore the right + // saved mask in that case). + } + else + memcpy(&stack_frame.ucontext.uc_sigmask, &signal_mask, sizeof(signal_mask)); memcpy(&stack_frame.ucontext.uc_stack, &signal_stack, sizeof(signal_stack)); EncodeMachineContext(&stack_frame.ucontext.uc_mcontext, &stopped_regs, intctx); @@ -787,7 +819,9 @@ retry_another_signal: goto retry_another_signal; } - // Update the current signal mask. + // Update the current signal mask. UpdatePendingSignals isn't called because + // sa_mask was or'd onto the current signal mask, only blocking signals, so + // nothing new can be delivered. memcpy(&signal_mask, &new_signal_mask, sizeof(sigset_t)); // Update the current alternate signal stack. @@ -818,6 +852,10 @@ retry_another_signal: if ( (signal_single = signal_count == 1) ) signal_single_frame = (uintptr_t) stack; + // If there was a saved signal mask, it will be restored once the signal + // handler complete, so forget it was ever saved. + has_saved_signal_mask = false; + // Run the signal handler by returning to user-space. return; } diff --git a/kernel/thread.cpp b/kernel/thread.cpp index b358b68a..9245580e 100644 --- a/kernel/thread.cpp +++ b/kernel/thread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2018 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -96,8 +96,10 @@ Thread::Thread() pledged_destruction = false; force_no_signals = false; signal_single = false; + has_saved_signal_mask = false; sigemptyset(&signal_pending); sigemptyset(&signal_mask); + sigemptyset(&saved_signal_mask); memset(&signal_stack, 0, sizeof(signal_stack)); signal_stack.ss_flags = SS_DISABLE; // execute_clock initialized in member constructor.