git: 910aa553a9ba - stable/13 - killpg(): close a race with fork(), part 2
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 07 Aug 2023 00:59:52 UTC
The branch stable/13 has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=910aa553a9baaa66030b40dc56c254bcc74a9286 commit 910aa553a9baaa66030b40dc56c254bcc74a9286 Author: Konstantin Belousov <kib@FreeBSD.org> AuthorDate: 2023-07-20 21:19:03 +0000 Commit: Konstantin Belousov <kib@FreeBSD.org> CommitDate: 2023-08-07 00:48:45 +0000 killpg(): close a race with fork(), part 2 (cherry picked from commit 232b922cb363e01ac0dd2a277d93cf74d8485e79) --- sys/kern/kern_fork.c | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 6fb9d2a83c8f..3b612e7b8990 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -857,7 +857,7 @@ fork1(struct thread *td, struct fork_req *fr) static int curfail; static struct timeval lastfail; int flags, pages; - bool killsx_locked; + bool killsx_locked, singlethreaded; flags = fr->fr_flags; pages = fr->fr_pages; @@ -915,6 +915,7 @@ fork1(struct thread *td, struct fork_req *fr) newproc = NULL; vm2 = NULL; killsx_locked = false; + singlethreaded = false; /* * Increment the nprocs resource before allocations occur. @@ -945,14 +946,37 @@ fork1(struct thread *td, struct fork_req *fr) } /* - * Atomically check for signals and block threads from sending - * a signal to our process group until the child is visible. + * If we are possibly multi-threaded, and there is a process + * sending a signal to our group right now, ensure that our + * other threads cannot be chosen for the signal queueing. + * Otherwise, this might delay signal action, and make the new + * child escape the signaling. */ pg = p1->p_pgrp; - if (sx_slock_sig(&pg->pg_killsx) != 0) { + if (p1->p_numthreads > 1) { + if (sx_try_slock(&pg->pg_killsx) != 0) { + killsx_locked = true; + } else { + PROC_LOCK(p1); + if (thread_single(p1, SINGLE_BOUNDARY)) { + PROC_UNLOCK(p1); + error = ERESTART; + goto fail2; + } + PROC_UNLOCK(p1); + singlethreaded = true; + } + } + + /* + * Atomically check for signals and block processes from sending + * a signal to our process group until the child is visible. + */ + if (!killsx_locked && sx_slock_sig(&pg->pg_killsx) != 0) { error = ERESTART; goto fail2; - } else if (__predict_false(p1->p_pgrp != pg || sig_intr() != 0)) { + } + if (__predict_false(p1->p_pgrp != pg || sig_intr() != 0)) { /* * Either the process was moved to other process * group, or there is pending signal. sx_slock_sig() @@ -1056,8 +1080,8 @@ fork1(struct thread *td, struct fork_req *fr) } do_fork(td, fr, newproc, td2, vm2, fp_procdesc); - sx_sunlock(&pg->pg_killsx); - return (0); + error = 0; + goto cleanup; fail0: error = EAGAIN; #ifdef MAC @@ -1075,9 +1099,16 @@ fail2: fdrop(fp_procdesc, td); } atomic_add_int(&nprocs, -1); +cleanup: if (killsx_locked) sx_sunlock(&pg->pg_killsx); - pause("fork", hz / 2); + if (singlethreaded) { + PROC_LOCK(p1); + thread_single_end(p1, SINGLE_BOUNDARY); + PROC_UNLOCK(p1); + } + if (error != 0) + pause("fork", hz / 2); return (error); }