DragonFlyBSD Kernel Audit
DF-0032 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -550,7 +550,46 @@
 	} else if (flags & RFFDG) {
 		error = fdcopy(p1, &p2->p_fd);
 		if (error != 0) {
+			/*
+			 * DF-0032: fdcopy() failed (its M_NULLOK kmalloc of
+			 * struct filedesc returned NULL).  Fully tear down the
+			 * partially-built p2 and back out the resource charges.
+			 * Otherwise the SIDL orphan plus the nprocs counter and
+			 * the per-uid proc-count are leaked permanently (the
+			 * orphan has no lwp/parent and never reaches exit),
+			 * exhausting system-wide maxproc slots.
+			 */
 			error = ENOMEM;
+			if (p1->p_flags & P_PROFIL)
+				stopprofclock(p2);
+			if (p2->p_textnch.ncp)
+				cache_drop(&p2->p_textnch);
+			if (p2->p_textvp)
+				vrele(p2->p_textvp);
+			if (p2->p_sigacts &&
+			    refcount_release(&p2->p_sigacts->ps_refcnt))
+				kfree(p2->p_sigacts, M_SUBPROC);
+			if (p2->p_args &&
+			    refcount_release(&p2->p_args->ar_ref))
+				kfree(p2->p_args, M_PARGS);
+			{
+				struct ucred *cr;
+				spin_lock(&p2->p_spin);
+				cr = p2->p_ucred;
+				p2->p_ucred = NULL;
+				spin_unlock(&p2->p_spin);
+				crfree(cr);
+			}
+			proc_remove_allproc(p2);
+			if (p2->p_reaper)
+				reaper_drop(p2->p_reaper);
+			lwkt_reltoken(&p2->p_token);
+			kfree(p2->p_uidpcpu, M_SUBPROC);
+			kfree(p2, M_PROC);
+			p2 = NULL;	/* done: must not re-release p_token */
+			atomic_add_int(&nprocs, -1);
+			chgproccnt(lp1->lwp_thread->td_ucred->cr_ruidinfo,
+				   -1, 0);
 			goto done;
 		}
 		fdtol = NULL;
diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c
--- a/sys/kern/kern_proc.c
+++ b/sys/kern/kern_proc.c
@@ -1052,6 +1052,33 @@
 }
 
 /*
+ * DF-0032: Remove a process from the allproc master list (inverse of
+ * proc_add_allproc()).  Used by fork1() to tear down a partially-built
+ * process when an unrecoverable error occurs after the process was added
+ * to allproc but before it gained a sibling/parent linkage (so, unlike
+ * proc_remove_zombie(), no p_sibling removal is done).
+ *
+ * Caller must hold p->p_token.
+ */
+void
+proc_remove_allproc(struct proc *p)
+{
+	procglob_t *prg;
+	int n;
+
+	n = ALLPROC_HASH(p->p_pid);
+	prg = &procglob[n];
+
+	PSTALL(p, "rmallproc", 1);
+	lwkt_gettoken(&prg->proc_token);
+	PSTALL(p, "rmallproca", 1);
+	LIST_REMOVE(p, p_list);
+	if (pid_doms[p->p_pid % PIDSEL_DOMAINS] != (uint8_t)time_second)
+		pid_doms[p->p_pid % PIDSEL_DOMAINS] = (uint8_t)time_second;
+	lwkt_reltoken(&prg->proc_token);
+}
+
+/*
  * Calculate a new process pid.  This function is integrated into
  * proc_add_allproc() to guarentee that the new pid is not reused before
  * the new process can be added to the allproc list.
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -536,6 +536,7 @@
 
 int	enterpgrp (struct proc *p, pid_t pgid, int mksess);
 void	proc_add_allproc(struct proc *p);
+void	proc_remove_allproc(struct proc *p);
 void	proc_move_allproc_zombie(struct proc *);
 void	proc_remove_zombie(struct proc *);
 void	allproc_scan(int (*callback)(struct proc *, void *), void *data,