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);
}