git: cf60931c32d7 - stable/13 - fork: Suspend other threads if both RFPROC and RFMEM are not set

Mark Johnston markj at FreeBSD.org
Thu May 20 14:10:43 UTC 2021


The branch stable/13 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=cf60931c32d7c5401c69ff0081e049a100673444

commit cf60931c32d7c5401c69ff0081e049a100673444
Author:     Mark Johnston <markj at FreeBSD.org>
AuthorDate: 2021-05-13 12:33:23 +0000
Commit:     Mark Johnston <markj at FreeBSD.org>
CommitDate: 2021-05-20 13:16:47 +0000

    fork: Suspend other threads if both RFPROC and RFMEM are not set
    
    Otherwise, a multithreaded parent process may trigger races in
    vm_forkproc() if one thread calls rfork() with RFMEM set and another
    calls rfork() without RFMEM.
    
    Also simplify vm_forkproc() a bit, vmspace_unshare() already checks to
    see if the address space is shared.
    
    Reported by:    syzbot+0aa7c2bec74c4066c36f at syzkaller.appspotmail.com
    Reported by:    syzbot+ea84cb06937afeae609d at syzkaller.appspotmail.com
    Reviewed by:    kib
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D30220
    
    (cherry picked from commit 9246b3090cbc82c54350391601b9acef2aa9a625)
---
 sys/kern/kern_fork.c | 13 +++++++++----
 sys/vm/vm_glue.c     |  8 +++-----
 sys/vm/vm_map.c      |  4 ++++
 3 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 2a092b192878..0d0659b432fe 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -313,8 +313,13 @@ fork_norfproc(struct thread *td, int flags)
 	    ("fork_norfproc called with RFPROC set"));
 	p1 = td->td_proc;
 
-	if (((p1->p_flag & (P_HADTHREADS|P_SYSTEM)) == P_HADTHREADS) &&
-	    (flags & (RFCFDG | RFFDG))) {
+	/*
+	 * Quiesce other threads if necessary.  If RFMEM is not specified we
+	 * must ensure that other threads do not concurrently create a second
+	 * process sharing the vmspace, see vmspace_unshare().
+	 */
+	if ((p1->p_flag & (P_HADTHREADS | P_SYSTEM)) == P_HADTHREADS &&
+	    ((flags & (RFCFDG | RFFDG)) != 0 || (flags & RFMEM) == 0)) {
 		PROC_LOCK(p1);
 		if (thread_single(p1, SINGLE_BOUNDARY)) {
 			PROC_UNLOCK(p1);
@@ -350,8 +355,8 @@ fork_norfproc(struct thread *td, int flags)
 	}
 
 fail:
-	if (((p1->p_flag & (P_HADTHREADS|P_SYSTEM)) == P_HADTHREADS) &&
-	    (flags & (RFCFDG | RFFDG))) {
+	if ((p1->p_flag & (P_HADTHREADS | P_SYSTEM)) == P_HADTHREADS &&
+	    ((flags & (RFCFDG | RFFDG)) != 0 || (flags & RFMEM) == 0)) {
 		PROC_LOCK(p1);
 		thread_single_end(p1, SINGLE_BOUNDARY);
 		PROC_UNLOCK(p1);
diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c
index a2500828eae4..be741fd40199 100644
--- a/sys/vm/vm_glue.c
+++ b/sys/vm/vm_glue.c
@@ -550,11 +550,9 @@ vm_forkproc(struct thread *td, struct proc *p2, struct thread *td2,
 		 * COW locally.
 		 */
 		if ((flags & RFMEM) == 0) {
-			if (refcount_load(&p1->p_vmspace->vm_refcnt) > 1) {
-				error = vmspace_unshare(p1);
-				if (error)
-					return (error);
-			}
+			error = vmspace_unshare(p1);
+			if (error)
+				return (error);
 		}
 		cpu_fork(td, p2, td2, flags);
 		return (0);
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index b3288fce5114..f17342b2a545 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -4896,6 +4896,10 @@ vmspace_unshare(struct proc *p)
 	struct vmspace *newvmspace;
 	vm_ooffset_t fork_charge;
 
+	/*
+	 * The caller is responsible for ensuring that the reference count
+	 * cannot concurrently transition 1 -> 2.
+	 */
 	if (refcount_load(&oldvmspace->vm_refcnt) == 1)
 		return (0);
 	fork_charge = 0;


More information about the dev-commits-src-all mailing list