hurd: Signal code refactoring

This should not change the current behavior, although this fixes a few
minor bugs which were made apparent in the process of global signal
disposition work:

- Split into more functions
- Scope variables more restrictively
- Split out inner functions
- refactor check_pending_signals
- make sigsuspend POSIX-conformant.
- fix uninitialized act value.
This commit is contained in:
Jeremie Koenig 2019-12-29 17:18:04 +01:00 committed by Samuel Thibault
parent a678c13b8f
commit 4288c548da
1 changed files with 160 additions and 112 deletions

View File

@ -455,6 +455,30 @@ abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
} }
} }
/* Wake up any sigsuspend call that is blocking SS->thread. SS must be
locked. */
static void
wake_sigsuspend (struct hurd_sigstate *ss)
{
error_t err;
mach_msg_header_t msg;
if (ss->suspended == MACH_PORT_NULL)
return;
/* There is a sigsuspend waiting. Tell it to wake up. */
msg.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0);
msg.msgh_remote_port = ss->suspended;
msg.msgh_local_port = MACH_PORT_NULL;
/* These values do not matter. */
msg.msgh_id = 8675309; /* Jenny, Jenny. */
ss->suspended = MACH_PORT_NULL;
err = __mach_msg (&msg, MACH_SEND_MSG, sizeof msg, 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
assert_perror (err);
}
struct hurd_signal_preemptor *_hurdsig_preemptors = 0; struct hurd_signal_preemptor *_hurdsig_preemptors = 0;
sigset_t _hurdsig_preempted_set; sigset_t _hurdsig_preempted_set;
@ -465,35 +489,18 @@ weak_alias (_hurdsig_preemptors, _hurdsig_preempters)
#define STOPSIGS (sigmask (SIGTTIN) | sigmask (SIGTTOU) \ #define STOPSIGS (sigmask (SIGTTIN) | sigmask (SIGTTOU) \
| sigmask (SIGSTOP) | sigmask (SIGTSTP)) | sigmask (SIGSTOP) | sigmask (SIGTSTP))
/* Deliver a signal. SS is not locked. */ /* Actual delivery of a single signal. Called with SS unlocked. When
void the signal is delivered, return 1 with SS locked. If the signal is
_hurd_internal_post_signal (struct hurd_sigstate *ss, being traced, return 0 with SS unlocked. */
int signo, struct hurd_signal_detail *detail, static int
mach_port_t reply_port, post_signal (struct hurd_sigstate *ss,
mach_msg_type_name_t reply_port_type, int signo, struct hurd_signal_detail *detail,
int untraced) int untraced, void (*reply) (void))
{ {
error_t err;
struct machine_thread_all_state thread_state; struct machine_thread_all_state thread_state;
enum { stop, ignore, core, term, handle } act; enum { stop, ignore, core, term, handle } act;
sighandler_t handler;
sigset_t pending;
int ss_suspended; int ss_suspended;
/* Reply to this sig_post message. */
__typeof (__msg_sig_post_reply) *reply_rpc
= (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply);
void reply (void)
{
error_t err;
if (reply_port == MACH_PORT_NULL)
return;
err = (*reply_rpc) (reply_port, reply_port_type, 0);
reply_port = MACH_PORT_NULL;
if (err != MACH_SEND_INVALID_DEST) /* Ignore dead reply port. */
assert_perror (err);
}
/* Mark the signal as pending. */ /* Mark the signal as pending. */
void mark_pending (void) void mark_pending (void)
{ {
@ -557,19 +564,23 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
ss_suspended = 1; ss_suspended = 1;
} }
error_t err;
sighandler_t handler;
if (signo == 0) if (signo == 0)
{ {
if (untraced) if (untraced)
/* This is PTRACE_CONTINUE. */ {
resume (); /* This is PTRACE_CONTINUE. */
act = ignore;
resume ();
}
/* This call is just to check for pending signals. */ /* This call is just to check for pending signals. */
__spin_lock (&ss->lock); __spin_lock (&ss->lock);
goto check_pending_signals; return 1;
} }
post_signal:
thread_state.set = 0; /* We know nothing. */ thread_state.set = 0; /* We know nothing. */
__spin_lock (&ss->lock); __spin_lock (&ss->lock);
@ -632,7 +643,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
suspend (); suspend ();
__spin_unlock (&ss->lock); __spin_unlock (&ss->lock);
reply (); reply ();
return; return 0;
} }
handler = ss->actions[signo].sa_handler; handler = ss->actions[signo].sa_handler;
@ -876,7 +887,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
as a unit. */ as a unit. */
crit ? 0 : signo, 1, crit ? 0 : signo, 1,
&thread_state, &state_changed, &thread_state, &state_changed,
&reply) reply)
!= MACH_PORT_NULL); != MACH_PORT_NULL);
if (crit) if (crit)
@ -963,6 +974,9 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
&& signo != SIGILL && signo != SIGTRAP) && signo != SIGILL && signo != SIGTRAP)
ss->actions[signo].sa_handler = SIG_DFL; ss->actions[signo].sa_handler = SIG_DFL;
/* Any sigsuspend call must return after the handler does. */
wake_sigsuspend (ss);
/* Start the thread running the handler (or possibly waiting for an /* Start the thread running the handler (or possibly waiting for an
RPC reply before running the handler). */ RPC reply before running the handler). */
err = __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR, err = __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,
@ -976,95 +990,129 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
} }
} }
/* The signal has either been ignored or is now being handled. We can return 1;
consider it delivered and reply to the killer. */ }
reply ();
/* We get here unless the signal was fatal. We still hold SS->lock. /* Return the set of pending signals in SS which should be delivered. */
Check for pending signals, and loop to post them. */ static sigset_t
{ pending_signals (struct hurd_sigstate *ss)
/* Return nonzero if SS has any signals pending we should worry about. {
We don't worry about any pending signals if we are stopped, nor if /* We don't worry about any pending signals if we are stopped, nor if
SS is in a critical section. We are guaranteed to get a sig_post SS is in a critical section. We are guaranteed to get a sig_post
message before any of them become deliverable: either the SIGCONT message before any of them become deliverable: either the SIGCONT
signal, or a sig_post with SIGNO==0 as an explicit poll when the signal, or a sig_post with SIGNO==0 as an explicit poll when the
thread finishes its critical section. */ thread finishes its critical section. */
inline int signals_pending (void) if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock))
return 0;
return ss->pending & ~ss->blocked;
}
/* Post the specified pending signals in SS and return 1. If one of
them is traced, abort immediately and return 0. SS must be locked on
entry and will be unlocked in all cases. */
static int
post_pending (struct hurd_sigstate *ss, sigset_t pending, void (*reply) (void))
{
int signo;
struct hurd_signal_detail detail;
for (signo = 1; signo < NSIG; ++signo)
if (__sigismember (&pending, signo))
{ {
if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock)) __sigdelset (&ss->pending, signo);
detail = ss->pending_data[signo];
__spin_unlock (&ss->lock);
/* Will reacquire the lock, except if the signal is traced. */
if (! post_signal (ss, signo, &detail, 0, reply))
return 0; return 0;
return pending = ss->pending & ~ss->blocked;
} }
check_pending_signals: /* No more signals pending; SS->lock is still locked. */
untraced = 0; __spin_unlock (&ss->lock);
if (signals_pending ()) return 1;
{ }
for (signo = 1; signo < NSIG; ++signo)
if (__sigismember (&pending, signo))
{
deliver_pending:
__sigdelset (&ss->pending, signo);
*detail = ss->pending_data[signo];
__spin_unlock (&ss->lock);
goto post_signal;
}
}
/* No pending signals left undelivered for this thread. /* Post all the pending signals of all threads and return 1. If a traced
If we were sent signal 0, we need to check for pending signal is encountered, abort immediately and return 0. */
signals for all threads. */ static int
if (signo == 0) post_all_pending_signals (void (*reply) (void))
{ {
__spin_unlock (&ss->lock); struct hurd_sigstate *ss;
__mutex_lock (&_hurd_siglock); sigset_t pending = 0;
for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
{
__spin_lock (&ss->lock);
for (signo = 1; signo < NSIG; ++signo)
if (__sigismember (&ss->pending, signo)
&& (!__sigismember (&ss->blocked, signo)
/* We "deliver" immediately pending blocked signals whose
action might be to ignore, so that if ignored they are
dropped right away. */
|| ss->actions[signo].sa_handler == SIG_IGN
|| ss->actions[signo].sa_handler == SIG_DFL))
{
__mutex_unlock (&_hurd_siglock);
goto deliver_pending;
}
__spin_unlock (&ss->lock);
}
__mutex_unlock (&_hurd_siglock);
}
else
{
/* No more signals pending; SS->lock is still locked.
Wake up any sigsuspend call that is blocking SS->thread. */
if (ss->suspended != MACH_PORT_NULL)
{
/* There is a sigsuspend waiting. Tell it to wake up. */
error_t err;
mach_msg_header_t msg;
msg.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, 0);
msg.msgh_remote_port = ss->suspended;
msg.msgh_local_port = MACH_PORT_NULL;
/* These values do not matter. */
msg.msgh_id = 8675309; /* Jenny, Jenny. */
ss->suspended = MACH_PORT_NULL;
err = __mach_msg (&msg, MACH_SEND_MSG, sizeof msg, 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
assert_perror (err);
}
__spin_unlock (&ss->lock);
}
}
/* All pending signals delivered to all threads. for (;;)
Now we can send the reply message even for signal 0. */ {
reply (); __mutex_lock (&_hurd_siglock);
for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
{
__spin_lock (&ss->lock);
pending = pending_signals (ss);
if (pending)
/* post_pending() below will unlock SS. */
break;
__spin_unlock (&ss->lock);
}
__mutex_unlock (&_hurd_siglock);
if (! pending)
return 1;
if (! post_pending (ss, pending, reply))
return 0;
}
}
/* Deliver a signal. SS is not locked. */
void
_hurd_internal_post_signal (struct hurd_sigstate *ss,
int signo, struct hurd_signal_detail *detail,
mach_port_t reply_port,
mach_msg_type_name_t reply_port_type,
int untraced)
{
/* Reply to this sig_post message. */
__typeof (__msg_sig_post_reply) *reply_rpc
= (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply);
void reply (void)
{
error_t err;
if (reply_port == MACH_PORT_NULL)
return;
err = (*reply_rpc) (reply_port, reply_port_type, 0);
reply_port = MACH_PORT_NULL;
if (err != MACH_SEND_INVALID_DEST) /* Ignore dead reply port. */
assert_perror (err);
}
if (! post_signal (ss, signo, detail, untraced, reply))
return;
/* The signal was neither fatal nor traced. We still hold SS->lock. */
if (signo != 0)
{
/* The signal has either been ignored or is now being handled. We can
consider it delivered and reply to the killer. */
reply ();
/* Post any pending signals for this thread. */
if (! post_pending (ss, pending_signals (ss), reply))
return;
}
else
{
/* We need to check for pending signals for all threads. */
__spin_unlock (&ss->lock);
if (! post_all_pending_signals (reply))
return;
/* All pending signals delivered to all threads.
Now we can send the reply message even for signal 0. */
reply ();
}
} }
/* Decide whether REFPORT enables the sender to send us a SIGNO signal. /* Decide whether REFPORT enables the sender to send us a SIGNO signal.