git: 9246b3090cbc - main - fork: Suspend other threads if both RFPROC and RFMEM are not set

Mark Johnston markj at FreeBSD.org
Thu May 13 12:34:31 UTC 2021


The branch main has been updated by markj:

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

commit 9246b3090cbc82c54350391601b9acef2aa9a625
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-13 12:33:23 +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
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D30220
---
 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 6facf744456c..7cfb08246f9e 100644
--- a/sys/vm/vm_glue.c
+++ b/sys/vm/vm_glue.c
@@ -553,11 +553,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 d870fe3507fd..1ac4ccf72f11 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -4867,6 +4867,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