aboutsummaryrefslogtreecommitdiff
path: root/hurd
diff options
context:
space:
mode:
Diffstat (limited to 'hurd')
-rw-r--r--hurd/hurdsig.c113
1 files changed, 86 insertions, 27 deletions
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c
index 9620a00ca4..c5ce050c7a 100644
--- a/hurd/hurdsig.c
+++ b/hurd/hurdsig.c
@@ -667,9 +667,16 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
/* Nobody cares about this signal. */
break;
+ sigbomb:
+ /* We got a fault setting up the stack frame for the handler.
+ Nothing to do but die; BSD gets SIGILL in this case. */
+ sigcode = signo; /* XXX ? */
+ signo = SIGILL;
+ act = core;
+ /* FALLTHROUGH */
+
case term: /* Time to die. */
case core: /* And leave a rotting corpse. */
- nirvana:
/* Have the proc server stop all other threads in our task. */
err = __USEPORT (PROC, __proc_dostop (port, _hurd_msgport_thread));
assert_perror (err);
@@ -693,7 +700,7 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
case handle:
/* Call a handler for this signal. */
{
- struct sigcontext *scp;
+ struct sigcontext *scp, ocontext;
int wait_for_reply, state_changed;
/* Stop the thread and abort its pending RPC operations. */
@@ -710,19 +717,64 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
thread_get_state is never kosher before thread_abort. */
abort_thread (ss, &thread_state, NULL, 0, 0);
- wait_for_reply = (abort_rpcs (ss, signo, &thread_state, &state_changed,
- &reply_port, reply_port_type, untraced)
- != MACH_PORT_NULL);
+ if (ss->context)
+ {
+ /* We have a previous sigcontext that sigreturn was about
+ to restore when another signal arrived. */
+
+ mach_port_t *loc;
- if (ss->critical_section)
+ if (_hurdsig_catch_fault (SIGSEGV))
+ {
+ assert (_hurdsig_fault_sigcode >= (long int) ss->context &&
+ _hurdsig_fault_sigcode < (long int) (ss->context + 1));
+ /* We faulted reading the thread's stack. Forget that
+ context and pretend it wasn't there. It almost
+ certainly crash if this handler returns, but that's it's
+ problem. */
+ ss->context = NULL;
+ }
+ else
+ {
+ /* Copy the context from the thread's stack before
+ we start diddling the stack to set up the handler. */
+ ocontext = *ss->context;
+ ss->context = &ocontext;
+ }
+ _hurdsig_end_catch_fault ();
+
+ if (! machine_get_basic_state (ss->thread, &thread_state))
+ goto sigbomb;
+ loc = interrupted_reply_port_location (&thread_state);
+ if (loc && *loc != MACH_PORT_NULL)
+ /* This is the reply port for the context which called
+ sigreturn. Since we are abandoning that context entirely
+ and restoring SS->context instead, destroy this port. */
+ __mach_port_destroy (__mach_task_self (), *loc);
+
+ /* The thread was in sigreturn, not in any interruptible RPC. */
+ wait_for_reply = 0;
+
+ assert (! ss->critical_section);
+ }
+ else
{
- /* The thread is in a critical section. Mark the signal as
- pending. When it finishes the critical section, it will
- check for pending signals. */
- mark_pending ();
- assert (! state_changed);
- __thread_resume (ss->thread);
- break;
+ wait_for_reply = (abort_rpcs (ss, signo,
+ &thread_state, &state_changed,
+ &reply_port, reply_port_type,
+ untraced)
+ != MACH_PORT_NULL);
+
+ if (ss->critical_section)
+ {
+ /* The thread is in a critical section. Mark the signal as
+ pending. When it finishes the critical section, it will
+ check for pending signals. */
+ mark_pending ();
+ assert (! state_changed);
+ __thread_resume (ss->thread);
+ break;
+ }
}
/* Call the machine-dependent function to set the thread up
@@ -731,18 +783,10 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
signo, sigcode,
wait_for_reply, &thread_state);
if (scp == NULL)
- {
- /* We got a fault setting up the stack frame for the handler.
- Nothing to do but die; BSD gets SIGILL in this case. */
- sigcode = signo; /* XXX ? */
- signo = SIGILL;
- act = core;
- goto nirvana;
- }
+ goto sigbomb;
/* Set the machine-independent parts of the signal context. */
- scp->sc_error = sigerror;
{
/* Fetch the thread variable for the MiG reply port,
and set it to MACH_PORT_NULL. */
@@ -754,17 +798,32 @@ _hurd_internal_post_signal (struct hurd_sigstate *ss,
}
else
scp->sc_reply_port = MACH_PORT_NULL;
+
+ /* Save the intr_port in use by the interrupted code,
+ and clear the cell before running the trampoline. */
+ scp->sc_intr_port = ss->intr_port;
+ ss->intr_port = MACH_PORT_NULL;
+
+ if (ss->context)
+ {
+ /* After the handler runs we will restore to the state in
+ SS->context, not the state of the thread now. So restore
+ that context's reply port and intr port. */
+
+ scp->sc_reply_port = ss->context->sc_reply_port;
+ scp->sc_intr_port = ss->context->sc_intr_port;
+
+ ss->context = NULL;
+ }
}
+ /* Backdoor extra argument to signal handler. */
+ scp->sc_error = sigerror;
+
/* Block SIGNO and requested signals while running the handler. */
scp->sc_mask = ss->blocked;
ss->blocked |= __sigmask (signo) | ss->actions[signo].sa_mask;
- /* Save the intr_port in use by the interrupted code,
- and clear the cell before running the trampoline. */
- scp->sc_intr_port = ss->intr_port;
- ss->intr_port = MACH_PORT_NULL;
-
/* Start the thread running the handler (or possibly waiting for an
RPC reply before running the handler). */
err = __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,