diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -3286,6 +3286,49 @@ } } +static void +sig_prep_first_dbg_stop(struct thread *td, struct proc *p, sigset_t *sp) +{ + MPASS(td->td_proc == p); + PROC_LOCK_ASSERT(p, MA_OWNED); + + if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED && + (p->p_flag2 & P2_PTRACE_FSTP) != 0 && + SIGISMEMBER(*sp, SIGSTOP)) { + /* + * If debugger just attached, always consume SIGSTOP + * from ptrace(PT_ATTACH) first, to execute the + * debugger attach ritual in order. + */ + td->td_dbgflags |= TDB_FSTP; + SIGEMPTYSET(*sp); + SIGADDSET(*sp, SIGSTOP); + } +} + +bool +sig_handle_sigstop(struct thread *td) +{ + struct proc *p; + sigset_t sigpending; + enum sigstatus res; + + p = td->td_proc; + PROC_LOCK(p); + sigpending = td->td_sigqueue.sq_signals; + SIGSETOR(sigpending, p->p_sigqueue.sq_signals); + sig_prep_first_dbg_stop(td, p, &sigpending); + if (SIGISMEMBER(sigpending, SIGSTOP)) { + mtx_lock(&p->p_sigacts->ps_mtx); + res = sigprocess(td, SIGSTOP); + mtx_unlock(&p->p_sigacts->ps_mtx); + } else { + res = SIGSTATUS_IGNORE; + } + PROC_UNLOCK(p); + return (res == SIGSTATUS_HANDLED); +} + /* * If the current process has received a signal (should be caught or cause * termination, should interrupt current syscall), return the signal number. @@ -3336,19 +3379,7 @@ } } - if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED && - (p->p_flag2 & P2_PTRACE_FSTP) != 0 && - SIGISMEMBER(sigpending, SIGSTOP)) { - /* - * If debugger just attached, always consume - * SIGSTOP from ptrace(PT_ATTACH) first, to - * execute the debugger attach ritual in - * order. - */ - td->td_dbgflags |= TDB_FSTP; - SIGEMPTYSET(sigpending); - SIGADDSET(sigpending, SIGSTOP); - } + sig_prep_first_dbg_stop(td, p, &sigpending); SIG_FOREACH(sig, &sigpending) { switch (sigprocess(td, sig)) { diff --git a/sys/kern/subr_sleepqueue.c b/sys/kern/subr_sleepqueue.c --- a/sys/kern/subr_sleepqueue.c +++ b/sys/kern/subr_sleepqueue.c @@ -659,6 +659,38 @@ sleepq_switch(wchan, pri); } +static bool +sleepq_handle_sigstop(struct thread *td, const void *wchan) +{ + if ((td->td_flags & TDF_SBDRY) != 0 || + !(td_ast_pending(td, TDA_SIG) || + td_ast_pending(td, TDA_SUSPEND))) + return (false); + if (sig_handle_sigstop(td)) { +//printf("STS td %p td_flags %#x p_flag %#x\n", td, td->td_flags, td->td_proc->p_flag); + /* + * The thread's interruptible sleep was aborted in + * order to suspend the process. Propagating the + * aborted sleep to the userspace boundary causes + * spurious EINTR returns from syscalls. Instead, we + * can handle signal processing here, noting that the + * place where interruptible non-boundary sleep is + * allowed is not much different from the userret() + * place. + * + * Note that we check the current process and thread + * states without locks. Also, only stops can be + * handled this way, since it is not safe to exit + * sleeping thread, because it might own resources + * needing destructors. + */ + td->td_intrval = 0; + sleepq_lock(wchan); + return (true); + } + return (false); +} + /* * Block the current thread until it is awakened from its sleep queue * or it is interrupted by a signal. @@ -666,12 +698,19 @@ int sleepq_wait_sig(const void *wchan, int pri) { - int rcatch; + struct thread *td; + int rcatch, rvals; - rcatch = sleepq_catch_signals(wchan, pri); - if (rcatch) - return (rcatch); - return (sleepq_check_signals()); + td = curthread; + for (;;) { + rcatch = sleepq_catch_signals(wchan, pri); + if (rcatch != 0) + return (rcatch); + rvals = sleepq_check_signals(); + if (rvals != 0 && sleepq_handle_sigstop(td, wchan)) + continue; + return (rvals); + } } /* @@ -699,17 +738,24 @@ int sleepq_timedwait_sig(const void *wchan, int pri) { + struct thread *td; int rcatch, rvalt, rvals; - rcatch = sleepq_catch_signals(wchan, pri); - /* We must always call check_timeout() to clear sleeptimo. */ - rvalt = sleepq_check_timeout(); - rvals = sleepq_check_signals(); - if (rcatch) - return (rcatch); - if (rvals) - return (rvals); - return (rvalt); + td = curthread; + for (;;) { + rcatch = sleepq_catch_signals(wchan, pri); + /* We must always call check_timeout() to clear sleeptimo. */ + rvalt = sleepq_check_timeout(); + rvals = sleepq_check_signals(); + if (rcatch != 0) + return (rcatch); + if (rvals != 0) { + if (sleepq_handle_sigstop(td, wchan)) + continue; + return (rvals); + } + return (rvalt); + } } /* diff --git a/sys/sys/signalvar.h b/sys/sys/signalvar.h --- a/sys/sys/signalvar.h +++ b/sys/sys/signalvar.h @@ -397,6 +397,7 @@ int sig_ast_checksusp(struct thread *td); int sig_ast_needsigchk(struct thread *td); void sig_drop_caught(struct proc *p); +bool sig_handle_sigstop(struct thread *td); void sigexit(struct thread *td, int sig) __dead2; int sigev_findtd(struct proc *p, struct sigevent *sigev, struct thread **); void sigfastblock_clear(struct thread *td);