- Protect randompid and nprocs with the allproc_lock.
- Reorder fork1() to do malloc() and other blocking operations prior to acquiring the needed process locks. - The new process inherit's the credentials of curthread, not the credentials of the old process. - Document a really weird race that will come up with KSE allows multiple kernel threads per process.
This commit is contained in:
parent
d7aadbf9ce
commit
3fc755c118
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=95938
@ -193,17 +193,19 @@ sysctl_kern_randompid(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
int error, pid;
|
||||
|
||||
sx_xlock(&allproc_lock);
|
||||
pid = randompid;
|
||||
error = sysctl_handle_int(oidp, &pid, 0, req);
|
||||
if (error || !req->newptr)
|
||||
return (error);
|
||||
if (pid < 0 || pid > PID_MAX - 100) /* out of range */
|
||||
pid = PID_MAX - 100;
|
||||
else if (pid < 2) /* NOP */
|
||||
pid = 0;
|
||||
else if (pid < 100) /* Make it reasonable */
|
||||
pid = 100;
|
||||
randompid = pid;
|
||||
if (error == 0 && req->newptr != NULL) {
|
||||
if (pid < 0 || pid > PID_MAX - 100) /* out of range */
|
||||
pid = PID_MAX - 100;
|
||||
else if (pid < 2) /* NOP */
|
||||
pid = 0;
|
||||
else if (pid < 100) /* Make it reasonable */
|
||||
pid = 100;
|
||||
randompid = pid;
|
||||
}
|
||||
sx_xunlock(&allproc_lock);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@ -245,6 +247,8 @@ fork1(td, flags, procp)
|
||||
struct thread *td2;
|
||||
struct kse *ke2;
|
||||
struct ksegrp *kg2;
|
||||
struct sigacts *newsigacts;
|
||||
struct procsig *newprocsig;
|
||||
|
||||
GIANT_REQUIRED;
|
||||
|
||||
@ -292,6 +296,9 @@ fork1(td, flags, procp)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Allocate new proc. */
|
||||
newproc = uma_zalloc(proc_zone, M_WAITOK);
|
||||
|
||||
/*
|
||||
* Although process entries are dynamically created, we still keep
|
||||
* a global limit on the maximum number we will create. Don't allow
|
||||
@ -299,49 +306,35 @@ fork1(td, flags, procp)
|
||||
* exceed the limit. The variable nprocs is the current number of
|
||||
* processes, maxproc is the limit.
|
||||
*/
|
||||
uid = p1->p_ucred->cr_ruid;
|
||||
sx_xlock(&allproc_lock);
|
||||
uid = td->td_ucred->cr_ruid;
|
||||
if ((nprocs >= maxproc - 10 && uid != 0) || nprocs >= maxproc) {
|
||||
sx_xunlock(&allproc_lock);
|
||||
uma_zfree(proc_zone, newproc);
|
||||
tsleep(&forksleep, PUSER, "fork", hz / 2);
|
||||
return (EAGAIN);
|
||||
}
|
||||
/*
|
||||
* Increment the count of procs running with this uid. Don't allow
|
||||
* a nonprivileged user to exceed their current limit.
|
||||
*/
|
||||
PROC_LOCK(p1);
|
||||
ok = chgproccnt(td->td_ucred->cr_ruidinfo, 1,
|
||||
(uid != 0) ? p1->p_rlimit[RLIMIT_NPROC].rlim_cur : 0);
|
||||
PROC_UNLOCK(p1);
|
||||
if (!ok) {
|
||||
sx_xunlock(&allproc_lock);
|
||||
uma_zfree(proc_zone, newproc);
|
||||
tsleep(&forksleep, PUSER, "fork", hz / 2);
|
||||
return (EAGAIN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment the nprocs resource before blocking can occur. There
|
||||
* are hard-limits as to the number of processes that can run.
|
||||
*/
|
||||
nprocs++;
|
||||
|
||||
/*
|
||||
* Increment the count of procs running with this uid. Don't allow
|
||||
* a nonprivileged user to exceed their current limit.
|
||||
*/
|
||||
ok = chgproccnt(p1->p_ucred->cr_ruidinfo, 1,
|
||||
(uid != 0) ? p1->p_rlimit[RLIMIT_NPROC].rlim_cur : 0);
|
||||
if (!ok) {
|
||||
/*
|
||||
* Back out the process count
|
||||
*/
|
||||
nprocs--;
|
||||
tsleep(&forksleep, PUSER, "fork", hz / 2);
|
||||
return (EAGAIN);
|
||||
}
|
||||
|
||||
/* Allocate new proc. */
|
||||
newproc = uma_zalloc(proc_zone, M_WAITOK);
|
||||
|
||||
/*
|
||||
* Setup linkage for kernel based threading
|
||||
*/
|
||||
if((flags & RFTHREAD) != 0) {
|
||||
newproc->p_peers = p1->p_peers;
|
||||
p1->p_peers = newproc;
|
||||
newproc->p_leader = p1->p_leader;
|
||||
} else {
|
||||
newproc->p_peers = NULL;
|
||||
newproc->p_leader = newproc;
|
||||
}
|
||||
|
||||
newproc->p_vmspace = NULL;
|
||||
|
||||
/*
|
||||
* Find an unused process ID. We remember a range of unused IDs
|
||||
* ready to use (from lastpid+1 through pidchecked-1).
|
||||
@ -349,7 +342,6 @@ fork1(td, flags, procp)
|
||||
* If RFHIGHPID is set (used during system boot), do not allocate
|
||||
* low-numbered pids.
|
||||
*/
|
||||
sx_xlock(&allproc_lock);
|
||||
trypid = lastpid + 1;
|
||||
if (flags & RFHIGHPID) {
|
||||
if (trypid < 10) {
|
||||
@ -425,6 +417,33 @@ fork1(td, flags, procp)
|
||||
LIST_INSERT_HEAD(PIDHASH(p2->p_pid), p2, p_hash);
|
||||
sx_xunlock(&allproc_lock);
|
||||
|
||||
/*
|
||||
* Malloc things while we don't hold any locks.
|
||||
*/
|
||||
if (flags & RFSIGSHARE) {
|
||||
MALLOC(newsigacts, struct sigacts *,
|
||||
sizeof(struct sigacts), M_SUBPROC, M_WAITOK);
|
||||
newprocsig = NULL;
|
||||
} else {
|
||||
newsigacts = NULL;
|
||||
MALLOC(newprocsig, struct procsig *, sizeof(struct procsig),
|
||||
M_SUBPROC, M_WAITOK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy filedesc.
|
||||
* XXX: This is busted. fd*() need to not take proc
|
||||
* arguments or something.
|
||||
*/
|
||||
if (flags & RFCFDG)
|
||||
fd = fdinit(td);
|
||||
else if (flags & RFFDG) {
|
||||
FILEDESC_LOCK(p1->p_fd);
|
||||
fd = fdcopy(td);
|
||||
FILEDESC_UNLOCK(p1->p_fd);
|
||||
} else
|
||||
fd = fdshare(p1);
|
||||
|
||||
/*
|
||||
* Make a proc table entry for the new process.
|
||||
* Start by zeroing the section of proc that is zero-initialized,
|
||||
@ -445,7 +464,10 @@ fork1(td, flags, procp)
|
||||
bzero(&kg2->kg_startzero,
|
||||
(unsigned) RANGEOF(struct ksegrp, kg_startzero, kg_endzero));
|
||||
|
||||
mtx_init(&p2->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK);
|
||||
PROC_LOCK(p2);
|
||||
PROC_LOCK(p1);
|
||||
|
||||
bcopy(&p1->p_startcopy, &p2->p_startcopy,
|
||||
(unsigned) RANGEOF(struct proc, p_startcopy, p_endcopy));
|
||||
bcopy(&td->td_kse->ke_startcopy, &ke2->ke_startcopy,
|
||||
@ -455,7 +477,6 @@ fork1(td, flags, procp)
|
||||
bcopy(&td->td_ksegrp->kg_startcopy, &kg2->kg_startcopy,
|
||||
(unsigned) RANGEOF(struct ksegrp, kg_startcopy, kg_endcopy));
|
||||
#undef RANGEOF
|
||||
PROC_UNLOCK(p1);
|
||||
|
||||
/*
|
||||
* XXXKSE Theoretically only the running thread would get copied
|
||||
@ -464,8 +485,6 @@ fork1(td, flags, procp)
|
||||
*/
|
||||
proc_linkup(p2, kg2, ke2, td2);
|
||||
|
||||
mtx_init(&p2->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK);
|
||||
PROC_LOCK(p2);
|
||||
/* note.. XXXKSE no pcb or u-area yet */
|
||||
|
||||
/*
|
||||
@ -479,25 +498,35 @@ fork1(td, flags, procp)
|
||||
if (p1->p_sflag & PS_PROFIL)
|
||||
startprofclock(p2);
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
PROC_LOCK(p1);
|
||||
p2->p_ucred = crhold(p1->p_ucred);
|
||||
p2->p_ucred = crhold(td->td_ucred);
|
||||
td2->td_ucred = crhold(p2->p_ucred); /* XXXKSE */
|
||||
|
||||
/*
|
||||
* Setup linkage for kernel based threading
|
||||
*/
|
||||
if((flags & RFTHREAD) != 0) {
|
||||
/*
|
||||
* XXX: This assumes a leader is a parent or grandparent of
|
||||
* all processes in a task.
|
||||
*/
|
||||
if (p1->p_leader != p1)
|
||||
PROC_LOCK(p1->p_leader);
|
||||
p2->p_peers = p1->p_peers;
|
||||
p1->p_peers = p2;
|
||||
p2->p_leader = p1->p_leader;
|
||||
if (p1->p_leader != p1)
|
||||
PROC_UNLOCK(p1->p_leader);
|
||||
} else {
|
||||
p2->p_peers = NULL;
|
||||
p2->p_leader = p2;
|
||||
}
|
||||
|
||||
pargs_hold(p2->p_args);
|
||||
|
||||
if (flags & RFSIGSHARE) {
|
||||
p2->p_procsig = p1->p_procsig;
|
||||
p2->p_procsig->ps_refcnt++;
|
||||
if (p1->p_sigacts == &p1->p_uarea->u_sigacts) {
|
||||
struct sigacts *newsigacts;
|
||||
|
||||
PROC_UNLOCK(p1);
|
||||
PROC_UNLOCK(p2);
|
||||
/* Create the shared sigacts structure */
|
||||
MALLOC(newsigacts, struct sigacts *,
|
||||
sizeof(struct sigacts), M_SUBPROC, M_WAITOK);
|
||||
PROC_LOCK(p2);
|
||||
PROC_LOCK(p1);
|
||||
/*
|
||||
* Set p_sigacts to the new shared structure.
|
||||
* Note that this is updating p1->p_sigacts at the
|
||||
@ -505,15 +534,12 @@ fork1(td, flags, procp)
|
||||
* the shared p_procsig->ps_sigacts.
|
||||
*/
|
||||
p2->p_sigacts = newsigacts;
|
||||
newsigacts = NULL;
|
||||
*p2->p_sigacts = p1->p_uarea->u_sigacts;
|
||||
}
|
||||
} else {
|
||||
PROC_UNLOCK(p1);
|
||||
PROC_UNLOCK(p2);
|
||||
MALLOC(p2->p_procsig, struct procsig *, sizeof(struct procsig),
|
||||
M_SUBPROC, M_WAITOK);
|
||||
PROC_LOCK(p2);
|
||||
PROC_LOCK(p1);
|
||||
p2->p_procsig = newprocsig;
|
||||
newprocsig = NULL;
|
||||
bcopy(p1->p_procsig, p2->p_procsig, sizeof(*p2->p_procsig));
|
||||
p2->p_procsig->ps_refcnt = 1;
|
||||
p2->p_sigacts = NULL; /* finished in vm_forkproc() */
|
||||
@ -523,25 +549,13 @@ fork1(td, flags, procp)
|
||||
else
|
||||
p2->p_sigparent = SIGCHLD;
|
||||
|
||||
/* bump references to the text vnode (for procfs) */
|
||||
/* Bump references to the text vnode (for procfs) */
|
||||
p2->p_textvp = p1->p_textvp;
|
||||
PROC_UNLOCK(p1);
|
||||
PROC_UNLOCK(p2);
|
||||
if (p2->p_textvp)
|
||||
VREF(p2->p_textvp);
|
||||
|
||||
if (flags & RFCFDG)
|
||||
fd = fdinit(td);
|
||||
else if (flags & RFFDG) {
|
||||
FILEDESC_LOCK(p1->p_fd);
|
||||
fd = fdcopy(td);
|
||||
FILEDESC_UNLOCK(p1->p_fd);
|
||||
} else
|
||||
fd = fdshare(p1);
|
||||
sx_xlock(&proctree_lock);
|
||||
PGRP_LOCK(p1->p_pgrp);
|
||||
PROC_LOCK(p2);
|
||||
p2->p_fd = fd;
|
||||
PROC_UNLOCK(p1);
|
||||
PROC_UNLOCK(p2);
|
||||
|
||||
/*
|
||||
* If p_limit is still copy-on-write, bump refcnt,
|
||||
@ -549,7 +563,6 @@ fork1(td, flags, procp)
|
||||
* (If PL_SHAREMOD is clear, the structure is shared
|
||||
* copy-on-write.)
|
||||
*/
|
||||
PROC_LOCK(p1);
|
||||
if (p1->p_limit->p_lflags & PL_SHAREMOD)
|
||||
p2->p_limit = limcopy(p1->p_limit);
|
||||
else {
|
||||
@ -557,6 +570,11 @@ fork1(td, flags, procp)
|
||||
p2->p_limit->p_refcnt++;
|
||||
}
|
||||
|
||||
sx_xlock(&proctree_lock);
|
||||
PGRP_LOCK(p1->p_pgrp);
|
||||
PROC_LOCK(p2);
|
||||
PROC_LOCK(p1);
|
||||
|
||||
/*
|
||||
* Preserve some more flags in subprocess. PS_PROFIL has already
|
||||
* been preserved.
|
||||
@ -570,36 +588,13 @@ fork1(td, flags, procp)
|
||||
p2->p_flag |= P_PPWAIT;
|
||||
|
||||
LIST_INSERT_AFTER(p1, p2, p_pglist);
|
||||
PROC_UNLOCK(p1);
|
||||
PROC_UNLOCK(p2);
|
||||
PGRP_UNLOCK(p1->p_pgrp);
|
||||
sx_xunlock(&proctree_lock);
|
||||
|
||||
/*
|
||||
* Attach the new process to its parent.
|
||||
*
|
||||
* If RFNOWAIT is set, the newly created process becomes a child
|
||||
* of init. This effectively disassociates the child from the
|
||||
* parent.
|
||||
*/
|
||||
if (flags & RFNOWAIT)
|
||||
pptr = initproc;
|
||||
else
|
||||
pptr = p1;
|
||||
sx_xlock(&proctree_lock);
|
||||
PROC_LOCK(p2);
|
||||
p2->p_pptr = pptr;
|
||||
PROC_UNLOCK(p2);
|
||||
LIST_INSERT_HEAD(&pptr->p_children, p2, p_sibling);
|
||||
sx_xunlock(&proctree_lock);
|
||||
PROC_LOCK(p2);
|
||||
LIST_INIT(&p2->p_children);
|
||||
LIST_INIT(&td2->td_contested); /* XXXKSE only 1 thread? */
|
||||
|
||||
callout_init(&p2->p_itcallout, 0);
|
||||
callout_init(&td2->td_slpcallout, 1); /* XXXKSE */
|
||||
|
||||
PROC_LOCK(p1);
|
||||
#ifdef KTRACE
|
||||
/*
|
||||
* Copy traceflag and tracefile if enabled. If not inherited,
|
||||
@ -608,13 +603,8 @@ fork1(td, flags, procp)
|
||||
*/
|
||||
if ((p1->p_traceflag & KTRFAC_INHERIT) && p2->p_tracep == NULL) {
|
||||
p2->p_traceflag = p1->p_traceflag;
|
||||
if ((p2->p_tracep = p1->p_tracep) != NULL) {
|
||||
PROC_UNLOCK(p1);
|
||||
PROC_UNLOCK(p2);
|
||||
if ((p2->p_tracep = p1->p_tracep) != NULL)
|
||||
VREF(p2->p_tracep);
|
||||
PROC_LOCK(p2);
|
||||
PROC_LOCK(p1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -632,8 +622,39 @@ fork1(td, flags, procp)
|
||||
*/
|
||||
_PHOLD(p1);
|
||||
PROC_UNLOCK(p1);
|
||||
PROC_UNLOCK(p2);
|
||||
|
||||
/*
|
||||
* Attach the new process to its parent.
|
||||
*
|
||||
* If RFNOWAIT is set, the newly created process becomes a child
|
||||
* of init. This effectively disassociates the child from the
|
||||
* parent.
|
||||
*/
|
||||
if (flags & RFNOWAIT)
|
||||
pptr = initproc;
|
||||
else
|
||||
pptr = p1;
|
||||
p2->p_pptr = pptr;
|
||||
LIST_INSERT_HEAD(&pptr->p_children, p2, p_sibling);
|
||||
PROC_UNLOCK(p2);
|
||||
sx_xunlock(&proctree_lock);
|
||||
|
||||
/*
|
||||
* XXXKSE: In KSE, there would be a race here if one thread was
|
||||
* dieing due to a signal (or calling exit1() for that matter) while
|
||||
* another thread was calling fork1(). Not sure how KSE wants to work
|
||||
* around that. The problem is that up until the point above, if p1
|
||||
* gets killed, it won't find p2 in its list in order for it to be
|
||||
* reparented. Alternatively, we could add a new p_flag that gets set
|
||||
* before we reparent all the children that we check above and just
|
||||
* use init as our parent if that if that flag is set. (Either that
|
||||
* or abort the fork if the flag is set since our parent died trying
|
||||
* to fork us (which is evil)).
|
||||
*/
|
||||
|
||||
KASSERT(newprocsig == NULL, ("unused newprocsig"));
|
||||
if (newsigacts != NULL)
|
||||
FREE(newsigacts, M_SUBPROC);
|
||||
/*
|
||||
* Finish creating the child process. It will return via a different
|
||||
* execution path later. (ie: directly into user mode)
|
||||
|
Loading…
Reference in New Issue
Block a user