DF-0032 / fix.diff
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, |