From: Claudio Jeker Subject: Re: fix stop signal delivery To: tech@openbsd.org Date: Mon, 10 Feb 2025 07:49:39 +0100 On Tue, Feb 04, 2025 at 03:02:06PM +0100, Claudio Jeker wrote: > Long story short, stop signals (SIGSTOP and SIGTTIN, SIGTTOU and SIGTSTP) > are busted for multithreaded processes (e.g. mpv needs workarounds because > of that). > > This diff fixes this by totally reworking the way processes are stopped. > Now this diff is too big to go in like this but it would be great if > people could try this out. Especially if you have problems with stop > signals or also when you debug multithreaded processes with egdb. Updated diff now that the first bits of this have been committed. -- :wq Claudio diff --git kern/kern_exec.c kern/kern_exec.c index de95acf6b19..fec6bec2a75 100644 --- kern/kern_exec.c +++ kern/kern_exec.c @@ -556,6 +556,7 @@ sys_execve(struct proc *p, void *v, register_t *retval) if (pr->ps_flags & PS_PPWAIT) { atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT); atomic_clearbits_int(&pr->ps_pptr->ps_flags, PS_ISPWAIT); + atomic_setbits_int(&pr->ps_pptr->ps_flags, PS_WAITEVENT); wakeup(pr->ps_pptr); } diff --git kern/kern_exit.c kern/kern_exit.c index aec356ade88..3e85863a183 100644 --- kern/kern_exit.c +++ kern/kern_exit.c @@ -148,6 +148,8 @@ exit1(struct proc *p, int xexit, int xsig, int flags) atomic_clearbits_int(&pr->ps_flags, PS_PPWAIT); atomic_clearbits_int(&pr->ps_pptr->ps_flags, PS_ISPWAIT); + atomic_setbits_int(&pr->ps_pptr->ps_flags, + PS_WAITEVENT); wakeup(pr->ps_pptr); } @@ -162,13 +164,11 @@ exit1(struct proc *p, int xexit, int xsig, int flags) pr->ps_exitcnt++; /* - * if somebody else wants to take us to single threaded mode, - * count ourselves out. + * if somebody else wants to take us to single threaded mode + * or stop us, count ourselves out. */ - if (pr->ps_single) { - if (--pr->ps_singlecnt == 0) - wakeup(&pr->ps_singlecnt); - } + if (pr->ps_single || ISSET(pr->ps_flags, PS_STOPPING)) + process_suspend_signal(pr); /* proc is off ps_threads list so update accounting of process now */ tuagg_add_runtime(); @@ -358,6 +358,7 @@ exit1(struct proc *p, int xexit, int xsig, int flags) if (pr->ps_flags & PS_NOZOMBIE) { struct process *ppr = pr->ps_pptr; process_reparent(pr, initprocess); + atomic_setbits_int(&ppr->ps_flags, PS_WAITEVENT); wakeup(ppr); } mtx_leave(&pr->ps_mtx); @@ -486,6 +487,8 @@ reaper(void *arg) if (pr->ps_flags & PS_ZOMBIE) { /* Post SIGCHLD and wake up parent. */ prsignal(pr->ps_pptr, SIGCHLD); + atomic_setbits_int(&pr->ps_pptr->ps_flags, + PS_WAITEVENT); wakeup(pr->ps_pptr); } else { /* No one will wait for us, just zap it. */ @@ -508,15 +511,19 @@ dowait6(struct proc *q, idtype_t idtype, id_t id, int *statusp, int options, memset(info, 0, sizeof(*info)); loop: + atomic_clearbits_int(&q->p_p->ps_flags, PS_WAITEVENT); nfound = 0; LIST_FOREACH(pr, &q->p_p->ps_children, ps_sibling) { + mtx_enter(&pr->ps_mtx); if ((pr->ps_flags & PS_NOZOMBIE) || (idtype == P_PID && id != pr->ps_pid) || - (idtype == P_PGID && id != pr->ps_pgid)) + (idtype == P_PGID && id != pr->ps_pgid)) { + mtx_leave(&pr->ps_mtx); continue; - + } nfound++; if ((options & WEXITED) && (pr->ps_flags & PS_ZOMBIE)) { + mtx_leave(&pr->ps_mtx); *retval = pr->ps_pid; if (info != NULL) { info->si_pid = pr->ps_pid; @@ -545,10 +552,9 @@ loop: } if ((options & WTRAPPED) && (pr->ps_flags & PS_TRACED) && (pr->ps_flags & PS_WAITED) == 0 && + (pr->ps_flags & PS_STOPPED) && (pr->ps_flags & PS_TRAPPED)) { - if (single_thread_wait(pr, 0)) - goto loop; - + mtx_leave(&pr->ps_mtx); if ((options & WNOWAIT) == 0) atomic_setbits_int(&pr->ps_flags, PS_WAITED); @@ -571,6 +577,7 @@ loop: (pr->ps_flags & PS_WAITED) == 0 && (pr->ps_flags & PS_STOPPED) && (pr->ps_flags & PS_TRAPPED) == 0) { + mtx_leave(&pr->ps_mtx); if ((options & WNOWAIT) == 0) atomic_setbits_int(&pr->ps_flags, PS_WAITED); @@ -590,6 +597,7 @@ loop: return (0); } if ((options & WCONTINUED) && (pr->ps_flags & PS_CONTINUED)) { + mtx_leave(&pr->ps_mtx); if ((options & WNOWAIT) == 0) atomic_clearbits_int(&pr->ps_flags, PS_CONTINUED); @@ -609,6 +617,7 @@ loop: memset(rusage, 0, sizeof(*rusage)); return (0); } + mtx_leave(&pr->ps_mtx); } /* * Look in the orphans list too, to allow the parent to @@ -638,7 +647,9 @@ loop: *retval = 0; return (0); } - if ((error = tsleep_nsec(q->p_p, PWAIT | PCATCH, "wait", INFSLP)) != 0) + sleep_setup(q->p_p, PWAIT | PCATCH, "wait"); + if ((error = sleep_finish(0, + !ISSET(READ_ONCE(q->p_p->ps_flags), PS_WAITEVENT))) != 0) return (error); goto loop; } @@ -746,6 +757,7 @@ proc_finish_wait(struct proc *waiter, struct process *pr) process_reparent(pr, tr); mtx_leave(&pr->ps_mtx); prsignal(tr, SIGCHLD); + atomic_setbits_int(&tr->ps_flags, PS_WAITEVENT); wakeup(tr); } else { mtx_leave(&pr->ps_mtx); diff --git kern/kern_fork.c kern/kern_fork.c index 33a85a45fe3..61bc434d8b4 100644 --- kern/kern_fork.c +++ kern/kern_fork.c @@ -593,8 +593,8 @@ thread_fork(struct proc *curp, void *stack, void *tcb, pid_t *tidptr, * if somebody else wants to take us to single threaded mode, * count ourselves in. */ - if (pr->ps_single) { - pr->ps_singlecnt++; + if (pr->ps_single || ISSET(pr->ps_flags, PS_STOPPING)) { + pr->ps_suspendcnt++; atomic_setbits_int(&p->p_flag, P_SUSPSINGLE); } mtx_leave(&pr->ps_mtx); diff --git kern/kern_proc.c kern/kern_proc.c index 7abbab5fd48..2ce1f577199 100644 --- kern/kern_proc.c +++ kern/kern_proc.c @@ -501,7 +501,7 @@ proc_printit(struct proc *p, const char *modif, p->p_runpri, p->p_usrpri, p->p_slppri, p->p_p->ps_nice); (*pr)(" wchan=%p, wmesg=%s, ps_single=%p scnt=%d ecnt=%d\n", p->p_wchan, (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "", - p->p_p->ps_single, p->p_p->ps_singlecnt, p->p_p->ps_exitcnt); + p->p_p->ps_single, p->p_p->ps_suspendcnt, p->p_p->ps_exitcnt); (*pr)(" forw=%p, list=%p,%p\n", TAILQ_NEXT(p, p_runq), p->p_list.le_next, p->p_list.le_prev); (*pr)(" process=%p user=%p, vmspace=%p\n", diff --git kern/kern_sig.c kern/kern_sig.c index b3ebcee6686..e5649febda1 100644 --- kern/kern_sig.c +++ kern/kern_sig.c @@ -121,12 +121,9 @@ const int sigprop[NSIG] = { void setsigvec(struct proc *, int, struct sigaction *); int proc_trap(struct proc *, int); -void proc_stop(struct proc *p, int); -void proc_stop_sweep(void *); -void *proc_stop_si; +void proc_stop(struct proc *p); void process_continue(struct process *, int); -void process_stop(struct process *, int, int); void setsigctx(struct proc *, int, struct sigctx *); void postsig_done(struct proc *, int, sigset_t, int); @@ -134,6 +131,7 @@ void postsig(struct proc *, int, struct sigctx *); int cansignal(struct proc *, struct process *, int); void ptsignal_locked(struct proc *, int, enum signal_type); +int proc_suspend_check_locked(struct proc *, int); struct pool sigacts_pool; /* memory pool for sigacts structures */ @@ -203,11 +201,6 @@ cansignal(struct proc *p, struct process *qr, int signum) void signal_init(void) { - proc_stop_si = softintr_establish(IPL_SOFTCLOCK, proc_stop_sweep, - NULL); - if (proc_stop_si == NULL) - panic("signal_init failed to register softintr"); - pool_init(&sigacts_pool, sizeof(struct sigacts), 0, IPL_NONE, PR_WAITOK, "sigapl", NULL); } @@ -917,7 +910,6 @@ prsignal(struct process *pr, int signum) /* * type = SPROCESS process signal, can be diverted (sigwait()) * type = STHREAD thread signal, but should be propagated if unhandled - * type = SPROPAGATED propagated to this thread, so don't propagate again */ void ptsignal(struct proc *p, int signum, enum signal_type type) @@ -1009,8 +1001,7 @@ ptsignal_locked(struct proc *p, int signum, enum signal_type type) } } - if (type != SPROPAGATED) - knote_locked(&pr->ps_klist, NOTE_SIGNAL | signum); + knote_locked(&pr->ps_klist, NOTE_SIGNAL | signum); prop = sigprop[signum]; @@ -1058,20 +1049,11 @@ ptsignal_locked(struct proc *p, int signum, enum signal_type type) } /* * If delivered to process, mark as pending there. Continue and stop - * signals will be propagated to all threads. So they are always - * marked at thread level. + * signals are always marked at process level. */ siglist = (type == SPROCESS) ? &pr->ps_siglist : &p->p_siglist; if (prop & (SA_CONT | SA_STOP)) - siglist = &p->p_siglist; - - /* - * XXX delay processing of SA_STOP signals unless action == SIG_DFL? - */ - if (prop & SA_STOP && type != SPROPAGATED) - TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) - if (q != p) - ptsignal_locked(q, signum, SPROPAGATED); + siglist = &pr->ps_siglist; SCHED_LOCK(); @@ -1222,7 +1204,9 @@ ptsignal_locked(struct proc *p, int signum, enum signal_type type) goto out; mask = 0; pr->ps_xsig = signum; - proc_stop(p, 0); + atomic_setbits_int(&pr->ps_flags, PS_STOPPING); + process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND); + wakeparent = 1; goto out; } /* @@ -1269,8 +1253,15 @@ out: } SCHED_UNLOCK(); - if (wakeparent) - wakeup(pr->ps_pptr); + if (wakeparent) { + if (prop & SA_STOP) + process_suspend_signal(pr); + else { + atomic_setbits_int(&pr->ps_pptr->ps_flags, + PS_WAITEVENT); + wakeup(pr->ps_pptr); + } + } } /* fill the signal context which should be used by postsig() and issignal() */ @@ -1444,10 +1435,16 @@ cursig(struct proc *p, struct sigctx *sctx, int deep) * then clear the signal. */ if (sctx->sig_stop) { + mtx_enter(&pr->ps_mtx); pr->ps_xsig = signum; + atomic_setbits_int(&pr->ps_flags, PS_STOPPING); SCHED_LOCK(); - proc_stop(p, 1); + process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND); SCHED_UNLOCK(); + atomic_setbits_int(&p->p_flag, P_SUSPSIG); + process_suspend_signal(pr); + proc_stop(p); + mtx_leave(&pr->ps_mtx); break; } else if (prop & SA_IGNORE) { /* @@ -1497,21 +1494,42 @@ proc_trap(struct proc *p, int signum) { struct process *pr = p->p_p; - single_thread_set(p, SINGLE_SUSPEND | SINGLE_NOWAIT); - pr->ps_xsig = signum; + mtx_enter(&pr->ps_mtx); + /* + * Wait until any other suspend condition cleared, + * including other traps. + */ + proc_suspend_check_locked(p, 0); + atomic_setbits_int(&pr->ps_flags, PS_STOPPING | PS_TRAPPED); SCHED_LOCK(); - atomic_setbits_int(&pr->ps_flags, PS_TRAPPED); - proc_stop(p, 1); + process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND); + SCHED_UNLOCK(); + atomic_setbits_int(&p->p_flag, P_SUSPSIG); + pr->ps_xsig = signum; + pr->ps_trapped = p; + + process_suspend_signal(pr); + proc_stop(p); + /* + * Clear all flags for proc and process by hand here since ptrace + * just calls setrunnable on the thread without clearing anything. + */ + atomic_clearbits_int(&p->p_flag, P_SUSPSIG); atomic_clearbits_int(&pr->ps_flags, PS_WAITED | PS_STOPPED | PS_TRAPPED); - SCHED_UNLOCK(); signum = pr->ps_xsig; pr->ps_xsig = 0; - if ((p->p_flag & P_TRACESINGLE) == 0) - single_thread_clear(p); + pr->ps_trapped = NULL; + + if ((p->p_flag & P_TRACESINGLE) == 0) { + SCHED_LOCK(); + process_continue(pr, P_SUSPSIG); + SCHED_UNLOCK(); + } atomic_clearbits_int(&p->p_flag, P_TRACESINGLE); + mtx_leave(&pr->ps_mtx); return signum; } @@ -1552,7 +1570,8 @@ process_continue(struct process *pr, int flag) * Clearing either makes the thread runnable or puts * it back into some sleep queue. */ - if (q->p_stat == SSTOP) { + if (q->p_stat == SSTOP && + ISSET(q->p_flag, P_SUSPSIG | P_SUSPSINGLE) == 0) { if (q->p_wchan == NULL) setrunnable(q); else @@ -1576,10 +1595,10 @@ process_stop(struct process *pr, int flag, int mode) /* skip curproc if it is part of pr, caller takes care of that */ if (curproc->p_p == pr) { p = curproc; - KASSERT(ISSET(p->p_flag, P_SUSPSINGLE) == 0); + KASSERT(ISSET(p->p_flag, P_SUSPSIG | P_SUSPSINGLE) == 0); } - pr->ps_singlecnt = pr->ps_threadcnt; + pr->ps_suspendcnt = pr->ps_threadcnt; TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) { if (q == p) continue; @@ -1598,7 +1617,7 @@ process_stop(struct process *pr, int flag, int mode) unsleep(q); setrunnable(q); } else - --pr->ps_singlecnt; + --pr->ps_suspendcnt; break; case SSLEEP: /* if it's not interruptible, then just have to wait */ @@ -1606,7 +1625,7 @@ process_stop(struct process *pr, int flag, int mode) /* merely need to suspend? just stop it */ if (mode == SINGLE_SUSPEND) { q->p_stat = SSTOP; - --pr->ps_singlecnt; + --pr->ps_suspendcnt; } else { /* need to unwind or exit, so wake it */ unsleep(q); @@ -1632,50 +1651,58 @@ process_stop(struct process *pr, int flag, int mode) * on the run queue. */ void -proc_stop(struct proc *p, int sw) +proc_stop(struct proc *p) { struct process *pr = p->p_p; -#ifdef MULTIPROCESSOR - SCHED_ASSERT_LOCKED(); -#endif - /* do not stop exiting procs */ - if (ISSET(p->p_flag, P_WEXIT)) - return; + MUTEX_ASSERT_LOCKED(&pr->ps_mtx); - p->p_stat = SSTOP; - atomic_clearbits_int(&pr->ps_flags, PS_WAITED); - atomic_setbits_int(&pr->ps_flags, PS_STOPPING); - atomic_setbits_int(&p->p_flag, P_SUSPSIG); /* - * We need this soft interrupt to be handled fast. - * Extra calls to softclock don't hurt. + * XXX to mi_switch we need to release the ps_mtx but then + * another ptsignal can race against this thread and see + * it in the wrong state. So instead set p_stat while still + * holding the ps_mtx. + * We still have an issue when dropping the ps_mtx and grabbing + * the SCHED_LOCK but that can't be fixed. */ - softintr_schedule(proc_stop_si); - if (sw) - mi_switch(); + SCHED_LOCK(); + p->p_stat = SSTOP; + SCHED_UNLOCK(); + mtx_leave(&pr->ps_mtx); + SCHED_LOCK(); + mi_switch(); + SCHED_UNLOCK(); + mtx_enter(&pr->ps_mtx); } /* - * Called from a soft interrupt to send signals to the parents of stopped - * processes. - * We can't do this in proc_stop because it's called with nasty locks held - * and we would need recursive scheduler lock to deal with that. + * Signal either the parent process or the ps_single thread depending on + * the mode. Only do this if the suspendcnt dropped to 0. If curproc part + * of the process count it out first. */ void -proc_stop_sweep(void *v) +process_suspend_signal(struct process *pr) { - struct process *pr; + MUTEX_ASSERT_LOCKED(&pr->ps_mtx); - LIST_FOREACH(pr, &allprocess, ps_list) { - if ((pr->ps_flags & PS_STOPPING) == 0) - continue; + /* if part of the process, count us out */ + if (curproc->p_p == pr) + --pr->ps_suspendcnt; + + if (pr->ps_suspendcnt != 0) + return; + + if (pr->ps_single == NULL) { + atomic_clearbits_int(&pr->ps_flags, + PS_STOPPING | PS_WAITED | PS_CONTINUED); atomic_setbits_int(&pr->ps_flags, PS_STOPPED); - atomic_clearbits_int(&pr->ps_flags, PS_STOPPING); if ((pr->ps_pptr->ps_sigacts->ps_sigflags & SAS_NOCLDSTOP) == 0) prsignal(pr->ps_pptr, SIGCHLD); + atomic_setbits_int(&pr->ps_pptr->ps_flags, PS_WAITEVENT); wakeup(pr->ps_pptr); + } else { + wakeup(&pr->ps_suspendcnt); } } @@ -2145,7 +2172,7 @@ userret(struct proc *p) struct sigctx ctx; int signum; - if (p->p_flag & P_SUSPSINGLE) + if (p->p_flag & (P_SUSPSINGLE | P_SUSPSIG)) proc_suspend_check(p, 0); /* send SIGPROF or SIGVTALRM if their timers interrupted this thread */ @@ -2189,7 +2216,8 @@ proc_suspend_check_locked(struct proc *p, int deep) MUTEX_ASSERT_LOCKED(&pr->ps_mtx); - if (pr->ps_single == NULL || pr->ps_single == p) + if ((pr->ps_single == NULL || pr->ps_single == p) && + !ISSET(pr->ps_flags, PS_STOPPING)) return (0); /* if we're in deep, we need to unwind to the edge */ @@ -2214,18 +2242,11 @@ proc_suspend_check_locked(struct proc *p, int deep) /* NOTREACHED */ } - if (--pr->ps_singlecnt == 0) - wakeup(&pr->ps_singlecnt); + process_suspend_signal(pr); /* not exiting and don't need to unwind, so suspend */ - mtx_leave(&pr->ps_mtx); - - SCHED_LOCK(); - p->p_stat = SSTOP; - mi_switch(); - SCHED_UNLOCK(); - mtx_enter(&pr->ps_mtx); - } while (pr->ps_single != NULL); + proc_stop(p); + } while (pr->ps_single != NULL || ISSET(pr->ps_flags, PS_STOPPING)); return (0); } @@ -2288,12 +2309,10 @@ single_thread_set(struct proc *p, int flags) SCHED_UNLOCK(); /* count ourself out */ - --pr->ps_singlecnt; + --pr->ps_suspendcnt; mtx_leave(&pr->ps_mtx); - if ((flags & SINGLE_NOWAIT) == 0) - single_thread_wait(pr, 1); - + single_thread_wait(pr, 1); return 0; } @@ -2309,8 +2328,8 @@ single_thread_wait(struct process *pr, int recheck) /* wait until they're all suspended */ mtx_enter(&pr->ps_mtx); - while ((wait = pr->ps_singlecnt > 0)) { - msleep_nsec(&pr->ps_singlecnt, &pr->ps_mtx, PWAIT, "suspend", + while ((wait = pr->ps_suspendcnt > 0)) { + msleep_nsec(&pr->ps_suspendcnt, &pr->ps_mtx, PWAIT, "suspend", INFSLP); if (!recheck) break; diff --git kern/kern_synch.c kern/kern_synch.c index 8548aea1c87..db97fde0c29 100644 --- kern/kern_synch.c +++ kern/kern_synch.c @@ -64,8 +64,6 @@ int sleep_signal_check(struct proc *, int); -extern void proc_stop(struct proc *p, int); - /* * We're only looking at 7 bits of the address; everything is * aligned to 4, lots of things are aligned to greater powers @@ -458,6 +456,7 @@ sleep_finish(int timo, int do_sleep) int sleep_signal_check(struct proc *p, int after_sleep) { + struct process *pr = p->p_p; struct sigctx ctx; int err, sig; @@ -467,24 +466,38 @@ sleep_signal_check(struct proc *p, int after_sleep) /* requested to stop */ if (!after_sleep) { - mtx_enter(&p->p_p->ps_mtx); - if (--p->p_p->ps_singlecnt == 0) - wakeup(&p->p_p->ps_singlecnt); - mtx_leave(&p->p_p->ps_mtx); + mtx_enter(&pr->ps_mtx); + process_suspend_signal(pr); SCHED_LOCK(); p->p_stat = SSTOP; SCHED_UNLOCK(); + mtx_leave(&pr->ps_mtx); } } if ((sig = cursig(p, &ctx, 1)) != 0) { if (ctx.sig_stop) { if (!after_sleep) { - p->p_p->ps_xsig = sig; + mtx_enter(&pr->ps_mtx); + pr->ps_xsig = sig; + /* + * This is for stop signals delivered before + * sleep_setup() was called. We need to do the + * full dance here before going to sleep. + */ + atomic_clearbits_int(&p->p_siglist, + sigmask(sig)); + atomic_setbits_int(&pr->ps_flags, PS_STOPPING); + SCHED_LOCK(); + process_stop(pr, P_SUSPSIG, SINGLE_SUSPEND); + SCHED_UNLOCK(); + atomic_setbits_int(&p->p_flag, P_SUSPSIG); + process_suspend_signal(pr); SCHED_LOCK(); - proc_stop(p, 0); + p->p_stat = SSTOP; SCHED_UNLOCK(); + mtx_leave(&pr->ps_mtx); } } else if (ctx.sig_intr && !ctx.sig_ignore) return EINTR; diff --git kern/sys_process.c kern/sys_process.c index d06b8f9e19a..365e44258c7 100644 --- kern/sys_process.c +++ kern/sys_process.c @@ -330,12 +330,11 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data) case PT_ATTACH: case PT_DETACH: /* Find the process we're supposed to be operating on. */ - if ((tr = prfind(pid)) == NULL) { + if (pid > THREAD_PID_OFFSET) { error = ESRCH; goto fail; } - t = TAILQ_FIRST(&tr->ps_threads); - break; + /* FALLTHROUGH */ /* calls that accept a PID or a thread ID */ case PT_CONTINUE: @@ -466,16 +465,6 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data) * from where it stopped." */ - if (pid < THREAD_PID_OFFSET && tr->ps_single) - t = tr->ps_single; - else if (t == tr->ps_single) - atomic_setbits_int(&t->p_flag, P_TRACESINGLE); - else { - error = EINVAL; - goto fail; - } - - /* If the address parameter is not (int *)1, set the pc. */ if ((int *)addr != (int *)1) if ((error = process_set_pc(t, addr)) != 0) @@ -504,9 +493,6 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data) * from where it stopped." */ - if (pid < THREAD_PID_OFFSET && tr->ps_single) - t = tr->ps_single; - #ifdef PT_STEP /* * Stop single stepping. @@ -525,25 +511,29 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data) memset(tr->ps_ptstat, 0, sizeof(*tr->ps_ptstat)); /* Finally, deliver the requested signal (or none). */ - if (t->p_stat == SSTOP) { - tr->ps_xsig = data; + if (tr->ps_trapped == t) { SCHED_LOCK(); + if (pid >= THREAD_PID_OFFSET) + atomic_setbits_int(&t->p_flag, + P_TRACESINGLE); + tr->ps_xsig = data; unsleep(t); setrunnable(t); SCHED_UNLOCK(); - } else { + } else if (pid < THREAD_PID_OFFSET) { if (data != 0) - psignal(t, data); + ptsignal(t, data, SPROCESS); + } else { + /* can not signal a single thread */ + error = EINVAL; + goto fail; } break; case PT_KILL: - if (pid < THREAD_PID_OFFSET && tr->ps_single) - t = tr->ps_single; - /* just send the process a KILL signal. */ data = SIGKILL; - goto sendsig; /* in PT_CONTINUE, above. */ + goto sendsig; /* in PT_DETACH, above. */ case PT_ATTACH: /* @@ -625,9 +615,13 @@ ptrace_kstate(struct proc *p, int req, pid_t pid, void *addr) tr->ps_ptmask = pe->pe_set_event; break; case PT_GET_PROCESS_STATE: - if (tr->ps_single) - tr->ps_ptstat->pe_tid = - tr->ps_single->p_tid + THREAD_PID_OFFSET; + mtx_enter(&tr->ps_mtx); + if (tr->ps_trapped != NULL) + tr->ps_ptstat->pe_tid = tr->ps_trapped->p_tid + + THREAD_PID_OFFSET; + else + tr->ps_ptstat->pe_tid = 0; + mtx_leave(&tr->ps_mtx); memcpy(addr, tr->ps_ptstat, sizeof *tr->ps_ptstat); break; default: @@ -812,21 +806,26 @@ ptrace_ustate(struct proc *p, int req, pid_t pid, void *addr, int data, static inline struct process * process_tprfind(pid_t tpid, struct proc **tp) { - if (tpid > THREAD_PID_OFFSET) { - struct proc *t = tfind(tpid - THREAD_PID_OFFSET); + struct process *tr; + struct proc *t; + if (tpid > THREAD_PID_OFFSET) { + t = tfind(tpid - THREAD_PID_OFFSET); if (t == NULL) return NULL; - *tp = t; - return t->p_p; + tr = t->p_p; } else { - struct process *tr = prfind(tpid); - + tr = prfind(tpid); if (tr == NULL) return NULL; - *tp = TAILQ_FIRST(&tr->ps_threads); - return tr; + if (tr->ps_trapped != NULL) + t = tr->ps_trapped; + else + t = TAILQ_FIRST(&tr->ps_threads); } + + *tp = t; + return tr; } diff --git sys/proc.h sys/proc.h index dd4b8686cf9..a68e925e044 100644 --- sys/proc.h +++ sys/proc.h @@ -190,7 +190,8 @@ struct process { int ps_siglist; /* Signals pending for the process. */ struct proc *ps_single; /* [m] Thread for single-threading. */ - u_int ps_singlecnt; /* [m] Number of threads to suspend. */ + struct proc *ps_trapped; /* [m] Thread trapped for ptrace. */ + u_int ps_suspendcnt; /* [m] Number of threads to suspend. */ u_int ps_exitcnt; /* [m] Number of threads in exit1. */ int ps_traceflag; /* Kernel trace points. */ @@ -303,6 +304,7 @@ struct process { #define PS_CHROOT 0x01000000 /* Process is chrooted */ #define PS_NOBTCFI 0x02000000 /* No Branch Target CFI */ #define PS_ITIMER 0x04000000 /* Virtual interval timers running */ +#define PS_WAITEVENT 0x10000000 /* wait(2) event pending */ #define PS_CONTINUED 0x20000000 /* Continued proc not yet waited for */ #define PS_STOPPED 0x40000000 /* Stopped process */ #define PS_TRAPPED 0x80000000 /* Stopped due to tracing event */ @@ -313,8 +315,8 @@ struct process { "\013WAITED" "\014COREDUMP" "\015SINGLEEXIT" "\016SINGLEUNWIND" \ "\017NOZOMBIE" "\020STOPPING" "\021SYSTEM" "\022EMBRYO" "\023ZOMBIE" \ "\024NOBROADCASTKILL" "\025PLEDGE" "\026WXNEEDED" "\027EXECPLEDGE" \ - "\030ORPHAN" "\031CHROOT" "\032NOBTCFI" "\033ITIMER" "\036CONTINUED" \ - "\037STOPPED" "\040TRAPPED") + "\030ORPHAN" "\031CHROOT" "\032NOBTCFI" "\033ITIMER" "\035WAITEVENT" \ + "\036CONTINUED" "\037STOPPED" "\040TRAPPED") struct kcov_dev; struct lock_list_entry; @@ -596,13 +598,14 @@ refreshcreds(struct proc *p) #define SINGLE_MASK 0x0f /* extra flags for single_thread_set */ #define SINGLE_DEEP 0x10 /* call is in deep */ -#define SINGLE_NOWAIT 0x20 /* do not wait for other threads to stop */ int single_thread_set(struct proc *, int); int single_thread_wait(struct process *, int); void single_thread_clear(struct proc *); int proc_suspend_check(struct proc *, int); +void process_suspend_signal(struct process *); +void process_stop(struct process *, int, int); void child_return(void *); diff --git sys/signalvar.h sys/signalvar.h index b41ca3adf3c..dffc5e01567 100644 --- sys/signalvar.h +++ sys/signalvar.h @@ -89,7 +89,7 @@ struct sigacts { #define sigcantmask (sigmask(SIGKILL) | sigmask(SIGSTOP)) #ifdef _KERNEL -enum signal_type { SPROCESS, STHREAD, SPROPAGATED }; +enum signal_type { SPROCESS, STHREAD }; struct sigio_ref;