Add signal mask support to ppoll(2).

This commit is contained in:
Jonas 'Sortie' Termansen 2018-10-20 12:57:31 +02:00
parent 9993a1c0fc
commit 20c1f1d0d4
5 changed files with 131 additions and 29 deletions

View File

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

View File

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

View File

@ -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 <assert.h>
#include <errno.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <timespec.h>
@ -38,7 +39,9 @@
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/poll.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/signal.h>
#include <sortix/kernel/syscall.h>
#include <sortix/kernel/thread.h>
#include <sortix/kernel/time.h>
#include <sortix/kernel/timer.h>
@ -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<Descriptor> 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;
}

View File

@ -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(&notset, set);
sigandset(&thread->signal_mask, &thread->signal_mask, &notset);
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;
}

View File

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