fork: hold newly created processes
Mateusz Guzik
mjguzik at gmail.com
Sun Oct 5 10:29:18 UTC 2014
fork: hold newly created processes
Consumers of fork1 -> do_fork receive new proc pointer, but nothing
guarnatees its stability at that time.
New process could already exit and be waited for, in which case we get a
use after free.
This is a temporary fix.
diff --git a/sys/compat/linux/linux_fork.c b/sys/compat/linux/linux_fork.c
index 316cf2a..8ad33f8 100644
--- a/sys/compat/linux/linux_fork.c
+++ b/sys/compat/linux/linux_fork.c
@@ -95,6 +95,8 @@ linux_fork(struct thread *td, struct linux_fork_args *args)
sched_add(td2, SRQ_BORING);
thread_unlock(td2);
+ PRELE(p2);
+
return (0);
}
@@ -139,6 +141,7 @@ linux_vfork(struct thread *td, struct linux_vfork_args *args)
PROC_LOCK(p2);
while (p2->p_flag & P_PPWAIT)
cv_wait(&p2->p_pwait, &p2->p_mtx);
+ _PRELE(p2);
PROC_UNLOCK(p2);
return (0);
@@ -287,13 +290,14 @@ linux_clone(struct thread *td, struct linux_clone_args *args)
td->td_retval[0] = p2->p_pid;
td->td_retval[1] = 0;
+ PROC_LOCK(p2);
if (args->flags & LINUX_CLONE_VFORK) {
/* wait for the children to exit, ie. emulate vfork */
- PROC_LOCK(p2);
while (p2->p_flag & P_PPWAIT)
cv_wait(&p2->p_pwait, &p2->p_mtx);
- PROC_UNLOCK(p2);
}
+ _PRELE(p2);
+ PROC_UNLOCK(p2);
return (0);
}
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index 141d438..af905a5 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -832,6 +832,7 @@ create_init(const void *udata __unused)
audit_cred_proc1(newcred);
#endif
initproc->p_ucred = newcred;
+ _PRELE(initproc);
PROC_UNLOCK(initproc);
crfree(oldcred);
cred_update_thread(FIRST_THREAD_IN_PROC(initproc));
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 8135afb..596a28c 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -107,6 +107,7 @@ sys_fork(struct thread *td, struct fork_args *uap)
if (error == 0) {
td->td_retval[0] = p2->p_pid;
td->td_retval[1] = 0;
+ PRELE(p2);
}
return (error);
}
@@ -130,6 +131,7 @@ sys_pdfork(td, uap)
if (error == 0) {
td->td_retval[0] = p2->p_pid;
td->td_retval[1] = 0;
+ PRELE(p2);
error = copyout(&fd, uap->fdp, sizeof(fd));
}
return (error);
@@ -147,6 +149,7 @@ sys_vfork(struct thread *td, struct vfork_args *uap)
if (error == 0) {
td->td_retval[0] = p2->p_pid;
td->td_retval[1] = 0;
+ PRELE(p2);
}
return (error);
}
@@ -166,6 +169,8 @@ sys_rfork(struct thread *td, struct rfork_args *uap)
if (error == 0) {
td->td_retval[0] = p2 ? p2->p_pid : 0;
td->td_retval[1] = 0;
+ if (p2 != NULL)
+ PRELE(p2);
}
return (error);
}
@@ -359,7 +364,7 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
struct vmspace *vm2, int pdflags)
{
struct proc *p1, *pptr;
- int p2_held, trypid;
+ int trypid;
struct filedesc *fd;
struct filedesc_to_leader *fdtol;
struct sigacts *newsigacts;
@@ -367,7 +372,6 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
sx_assert(&proctree_lock, SX_SLOCKED);
sx_assert(&allproc_lock, SX_XLOCKED);
- p2_held = 0;
p1 = td->td_proc;
/*
@@ -674,6 +678,12 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
p2->p_state = PRS_NORMAL;
PROC_SUNLOCK(p2);
+ /*
+ * We return p2 pointer to the caller. Hold it so that it is
+ * guaranteed to remain valid.
+ */
+ _PHOLD(p2);
+
#ifdef KDTRACE_HOOKS
/*
* Tell the DTrace fasttrap provider about the new process so that any
@@ -696,8 +706,6 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
td->td_dbgflags |= TDB_FORK;
td->td_dbg_forked = p2->p_pid;
td2->td_dbgflags |= TDB_STOPATFORK;
- _PHOLD(p2);
- p2_held = 1;
}
if (flags & RFPPWAIT) {
td->td_pflags |= TDP_RFPPWAIT;
@@ -733,8 +741,6 @@ do_fork(struct thread *td, int flags, struct proc *p2, struct thread *td2,
PROC_LOCK(p2);
while ((td2->td_dbgflags & TDB_STOPATFORK) != 0)
cv_wait(&p2->p_dbgwait, &p2->p_mtx);
- if (p2_held)
- _PRELE(p2);
PROC_UNLOCK(p2);
}
diff --git a/sys/kern/kern_kthread.c b/sys/kern/kern_kthread.c
index 969c513..bb0e232 100644
--- a/sys/kern/kern_kthread.c
+++ b/sys/kern/kern_kthread.c
@@ -134,6 +134,8 @@ kproc_create(void (*func)(void *), void *arg,
sched_add(td, SRQ_BORING);
thread_unlock(td);
+ PRELE(p2);
+
return 0;
}
More information about the freebsd-hackers
mailing list