Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_fork.c
Show First 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
#include <sys/acct.h> | #include <sys/acct.h> | ||||
#include <sys/ktr.h> | #include <sys/ktr.h> | ||||
#include <sys/ktrace.h> | #include <sys/ktrace.h> | ||||
#include <sys/unistd.h> | #include <sys/unistd.h> | ||||
#include <sys/sdt.h> | #include <sys/sdt.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
#include <sys/sysent.h> | #include <sys/sysent.h> | ||||
#include <sys/signalvar.h> | #include <sys/signalvar.h> | ||||
#include <sys/vps.h> | |||||
#include <security/audit/audit.h> | #include <security/audit/audit.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_map.h> | #include <vm/vm_map.h> | ||||
#include <vm/vm_extern.h> | #include <vm/vm_extern.h> | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | sys_rfork(struct thread *td, struct rfork_args *uap) | ||||
error = fork1(td, &fr); | error = fork1(td, &fr); | ||||
if (error == 0) { | if (error == 0) { | ||||
td->td_retval[0] = pid; | td->td_retval[0] = pid; | ||||
td->td_retval[1] = 0; | td->td_retval[1] = 0; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
int nprocs = 1; /* process 0 */ | VPS_DEFINE(int, nprocs) = 1; /* process 0 */ | ||||
int lastpid = 0; | VPS_DEFINE(int, lastpid) = 0; | ||||
SYSCTL_INT(_kern, OID_AUTO, lastpid, CTLFLAG_RD, &lastpid, 0, | SYSCTL_INT(_kern, OID_AUTO, lastpid, CTLFLAG_RD|CTLFLAG_VPS, | ||||
"Last used PID"); | &VPS_NAME(lastpid), 0, "Last used PID"); | ||||
/* | /* | ||||
* Random component to lastpid generation. We mix in a random factor to make | * Random component to lastpid generation. We mix in a random factor to make | ||||
* it a little harder to predict. We sanity check the modulus value to avoid | * it a little harder to predict. We sanity check the modulus value to avoid | ||||
* doing it in critical paths. Don't let it be too small or we pointlessly | * doing it in critical paths. Don't let it be too small or we pointlessly | ||||
* waste randomness entropy, and don't let it be impossibly large. Using a | * waste randomness entropy, and don't let it be impossibly large. Using a | ||||
* modulus that is too big causes a LOT more process table scans and slows | * modulus that is too big causes a LOT more process table scans and slows | ||||
* down fork processing as the pidchecked caching is defeated. | * down fork processing as the pidchecked caching is defeated. | ||||
*/ | */ | ||||
static int randompid = 0; | static VPS_DEFINE(int, randompid) = 0; | ||||
#define V_randompid VPS(randompid) | |||||
static int | static int | ||||
sysctl_kern_randompid(SYSCTL_HANDLER_ARGS) | sysctl_kern_randompid(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error, pid; | int error, pid; | ||||
error = sysctl_wire_old_buffer(req, sizeof(int)); | error = sysctl_wire_old_buffer(req, sizeof(int)); | ||||
if (error != 0) | if (error != 0) | ||||
return(error); | return(error); | ||||
sx_xlock(&allproc_lock); | sx_xlock(&V_allproc_lock); | ||||
pid = randompid; | pid = V_randompid; | ||||
error = sysctl_handle_int(oidp, &pid, 0, req); | error = sysctl_handle_int(oidp, &pid, 0, req); | ||||
if (error == 0 && req->newptr != NULL) { | if (error == 0 && req->newptr != NULL) { | ||||
if (pid == 0) | if (pid == 0) | ||||
randompid = 0; | V_randompid = 0; | ||||
else if (pid == 1) | else if (pid == 1) | ||||
/* generate a random PID modulus between 100 and 1123 */ | /* generate a random PID modulus between 100 and 1123 */ | ||||
randompid = 100 + arc4random() % 1024; | V_randompid = 100 + arc4random() % 1024; | ||||
else if (pid < 0 || pid > pid_max - 100) | else if (pid < 0 || pid > pid_max - 100) | ||||
/* out of range */ | /* out of range */ | ||||
randompid = pid_max - 100; | V_randompid = pid_max - 100; | ||||
else if (pid < 100) | else if (pid < 100) | ||||
/* Make it reasonable */ | /* Make it reasonable */ | ||||
randompid = 100; | V_randompid = 100; | ||||
else | else | ||||
randompid = pid; | V_randompid = pid; | ||||
} | } | ||||
sx_xunlock(&allproc_lock); | sx_xunlock(&V_allproc_lock); | ||||
return (error); | return (error); | ||||
} | } | ||||
SYSCTL_PROC(_kern, OID_AUTO, randompid, CTLTYPE_INT|CTLFLAG_RW, | SYSCTL_PROC(_kern, OID_AUTO, randompid, CTLTYPE_INT|CTLFLAG_RW, | ||||
0, 0, sysctl_kern_randompid, "I", "Random PID modulus. Special values: 0: disable, 1: choose random value"); | 0, 0, sysctl_kern_randompid, "I", "Random PID modulus. Special values: 0: disable, 1: choose random value"); | ||||
static int | static VPS_DEFINE(int, pidchecked) = 0; | ||||
#define V_pidchecked VPS(pidchecked) | |||||
int | |||||
fork_findpid(int flags) | fork_findpid(int flags) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
int trypid; | int trypid; | ||||
static int pidchecked = 0; | |||||
/* | /* | ||||
* Requires allproc_lock in order to iterate over the list | * Requires allproc_lock in order to iterate over the list | ||||
* of processes, and proctree_lock to access p_pgrp. | * of processes, and proctree_lock to access p_pgrp. | ||||
*/ | */ | ||||
sx_assert(&allproc_lock, SX_LOCKED); | sx_assert(&V_allproc_lock, SX_LOCKED); | ||||
sx_assert(&proctree_lock, SX_LOCKED); | sx_assert(&V_proctree_lock, SX_LOCKED); | ||||
/* | /* | ||||
* Find an unused process ID. We remember a range of unused IDs | * Find an unused process ID. We remember a range of unused IDs | ||||
* ready to use (from lastpid+1 through pidchecked-1). | * ready to use (from lastpid+1 through pidchecked-1). | ||||
* | * | ||||
* If RFHIGHPID is set (used during system boot), do not allocate | * If RFHIGHPID is set (used during system boot), do not allocate | ||||
* low-numbered pids. | * low-numbered pids. | ||||
*/ | */ | ||||
trypid = lastpid + 1; | trypid = V_lastpid + 1; | ||||
if (flags & RFHIGHPID) { | if (flags & RFHIGHPID) { | ||||
if (trypid < 10) | if (trypid < 10) | ||||
trypid = 10; | trypid = 10; | ||||
} else { | } else { | ||||
if (randompid) | if (V_randompid) | ||||
trypid += arc4random() % randompid; | trypid += arc4random() % V_randompid; | ||||
} | } | ||||
retry: | retry: | ||||
/* | /* | ||||
* If the process ID prototype has wrapped around, | * If the process ID prototype has wrapped around, | ||||
* restart somewhat above 0, as the low-numbered procs | * restart somewhat above 0, as the low-numbered procs | ||||
* tend to include daemons that don't exit. | * tend to include daemons that don't exit. | ||||
*/ | */ | ||||
if (trypid >= pid_max) { | if (trypid >= pid_max) { | ||||
trypid = trypid % pid_max; | trypid = trypid % pid_max; | ||||
if (trypid < 100) | if (trypid < 100) | ||||
trypid += 100; | trypid += 100; | ||||
pidchecked = 0; | V_pidchecked = 0; | ||||
} | } | ||||
if (trypid >= pidchecked) { | if (trypid >= V_pidchecked) { | ||||
int doingzomb = 0; | int doingzomb = 0; | ||||
pidchecked = PID_MAX; | V_pidchecked = PID_MAX; | ||||
/* | /* | ||||
* Scan the active and zombie procs to check whether this pid | * Scan the active and zombie procs to check whether this pid | ||||
* is in use. Remember the lowest pid that's greater | * is in use. Remember the lowest pid that's greater | ||||
* than trypid, so we can avoid checking for a while. | * than trypid, so we can avoid checking for a while. | ||||
* | * | ||||
* Avoid reuse of the process group id, session id or | * Avoid reuse of the process group id, session id or | ||||
* the reaper subtree id. Note that for process group | * the reaper subtree id. Note that for process group | ||||
* and sessions, the amount of reserved pids is | * and sessions, the amount of reserved pids is | ||||
* limited by process limit. For the subtree ids, the | * limited by process limit. For the subtree ids, the | ||||
* id is kept reserved only while there is a | * id is kept reserved only while there is a | ||||
* non-reaped process in the subtree, so amount of | * non-reaped process in the subtree, so amount of | ||||
* reserved pids is limited by process limit times | * reserved pids is limited by process limit times | ||||
* two. | * two. | ||||
*/ | */ | ||||
p = LIST_FIRST(&allproc); | /* Operate on current vps instance only. */ | ||||
p = LIST_FIRST(&V_allproc); | |||||
again: | again: | ||||
for (; p != NULL; p = LIST_NEXT(p, p_list)) { | for (; p != NULL; p = LIST_NEXT(p, p_list)) { | ||||
while (p->p_pid == trypid || | while (p->p_pid == trypid || | ||||
p->p_reapsubtree == trypid || | p->p_reapsubtree == trypid || | ||||
(p->p_pgrp != NULL && | (p->p_pgrp != NULL && | ||||
(p->p_pgrp->pg_id == trypid || | (p->p_pgrp->pg_id == trypid || | ||||
(p->p_session != NULL && | (p->p_session != NULL && | ||||
p->p_session->s_sid == trypid)))) { | p->p_session->s_sid == trypid)))) { | ||||
trypid++; | trypid++; | ||||
if (trypid >= pidchecked) | if (trypid >= V_pidchecked) | ||||
goto retry; | goto retry; | ||||
} | } | ||||
if (p->p_pid > trypid && pidchecked > p->p_pid) | if (p->p_pid > trypid && V_pidchecked > p->p_pid) | ||||
pidchecked = p->p_pid; | V_pidchecked = p->p_pid; | ||||
if (p->p_pgrp != NULL) { | if (p->p_pgrp != NULL) { | ||||
if (p->p_pgrp->pg_id > trypid && | if (p->p_pgrp->pg_id > trypid && | ||||
pidchecked > p->p_pgrp->pg_id) | V_pidchecked > p->p_pgrp->pg_id) | ||||
pidchecked = p->p_pgrp->pg_id; | V_pidchecked = p->p_pgrp->pg_id; | ||||
if (p->p_session != NULL && | if (p->p_session != NULL && | ||||
p->p_session->s_sid > trypid && | p->p_session->s_sid > trypid && | ||||
pidchecked > p->p_session->s_sid) | V_pidchecked > p->p_session->s_sid) | ||||
pidchecked = p->p_session->s_sid; | V_pidchecked = p->p_session->s_sid; | ||||
} | } | ||||
} | } | ||||
if (!doingzomb) { | if (!doingzomb) { | ||||
/* Operate on current vps instance only. */ | |||||
doingzomb = 1; | doingzomb = 1; | ||||
p = LIST_FIRST(&zombproc); | p = LIST_FIRST(&V_zombproc); | ||||
goto again; | goto again; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* RFHIGHPID does not mess with the lastpid counter during boot. | * RFHIGHPID does not mess with the lastpid counter during boot. | ||||
*/ | */ | ||||
if (flags & RFHIGHPID) | if (flags & RFHIGHPID) | ||||
pidchecked = 0; | V_pidchecked = 0; | ||||
else | else | ||||
lastpid = trypid; | V_lastpid = trypid; | ||||
return (trypid); | return (trypid); | ||||
} | } | ||||
static int | static int | ||||
fork_norfproc(struct thread *td, int flags) | fork_norfproc(struct thread *td, int flags) | ||||
{ | { | ||||
int error; | int error; | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *td2, | ||||
struct vmspace *vm2, struct file *fp_procdesc) | struct vmspace *vm2, struct file *fp_procdesc) | ||||
{ | { | ||||
struct proc *p1, *pptr; | struct proc *p1, *pptr; | ||||
int trypid; | int trypid; | ||||
struct filedesc *fd; | struct filedesc *fd; | ||||
struct filedesc_to_leader *fdtol; | struct filedesc_to_leader *fdtol; | ||||
struct sigacts *newsigacts; | struct sigacts *newsigacts; | ||||
sx_assert(&proctree_lock, SX_LOCKED); | sx_assert(&V_proctree_lock, SX_LOCKED); | ||||
sx_assert(&allproc_lock, SX_XLOCKED); | sx_assert(&V_allproc_lock, SX_XLOCKED); | ||||
p1 = td->td_proc; | p1 = td->td_proc; | ||||
trypid = fork_findpid(fr->fr_flags); | trypid = fork_findpid(fr->fr_flags); | ||||
p2->p_state = PRS_NEW; /* protect against others */ | p2->p_state = PRS_NEW; /* protect against others */ | ||||
p2->p_pid = trypid; | p2->p_pid = trypid; | ||||
AUDIT_ARG_PID(p2->p_pid); | AUDIT_ARG_PID(p2->p_pid); | ||||
LIST_INSERT_HEAD(&allproc, p2, p_list); | LIST_INSERT_HEAD(&V_allproc, p2, p_list); | ||||
allproc_gen++; | allproc_gen++; | ||||
LIST_INSERT_HEAD(PIDHASH(p2->p_pid), p2, p_hash); | LIST_INSERT_HEAD(PIDHASH(p2->p_pid), p2, p_hash); | ||||
PROC_LOCK(p2); | PROC_LOCK(p2); | ||||
PROC_LOCK(p1); | PROC_LOCK(p1); | ||||
sx_xunlock(&allproc_lock); | sx_xunlock(&V_allproc_lock); | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&V_proctree_lock); | ||||
bcopy(&p1->p_startcopy, &p2->p_startcopy, | bcopy(&p1->p_startcopy, &p2->p_startcopy, | ||||
__rangeof(struct proc, p_startcopy, p_endcopy)); | __rangeof(struct proc, p_startcopy, p_endcopy)); | ||||
pargs_hold(p2->p_args); | pargs_hold(p2->p_args); | ||||
PROC_UNLOCK(p1); | PROC_UNLOCK(p1); | ||||
bzero(&p2->p_startzero, | bzero(&p2->p_startzero, | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | bcopy(&td->td_startcopy, &td2->td_startcopy, | ||||
__rangeof(struct thread, td_startcopy, td_endcopy)); | __rangeof(struct thread, td_startcopy, td_endcopy)); | ||||
bcopy(&p2->p_comm, &td2->td_name, sizeof(td2->td_name)); | bcopy(&p2->p_comm, &td2->td_name, sizeof(td2->td_name)); | ||||
td2->td_sigstk = td->td_sigstk; | td2->td_sigstk = td->td_sigstk; | ||||
td2->td_flags = TDF_INMEM; | td2->td_flags = TDF_INMEM; | ||||
td2->td_lend_user_pri = PRI_MAX; | td2->td_lend_user_pri = PRI_MAX; | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
td2->td_vps = TD_TO_VPS(td); | |||||
td2->td_vnet = NULL; | td2->td_vnet = NULL; | ||||
td2->td_vnet_lpush = NULL; | td2->td_vnet_lpush = NULL; | ||||
#endif | #endif | ||||
/* | /* | ||||
* Allow the scheduler to initialize the child. | * Allow the scheduler to initialize the child. | ||||
*/ | */ | ||||
thread_lock(td); | thread_lock(td); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | #endif | ||||
/* Bump references to the text vnode (for procfs). */ | /* Bump references to the text vnode (for procfs). */ | ||||
if (p2->p_textvp) | if (p2->p_textvp) | ||||
vrefact(p2->p_textvp); | vrefact(p2->p_textvp); | ||||
/* | /* | ||||
* Set up linkage for kernel based threading. | * Set up linkage for kernel based threading. | ||||
*/ | */ | ||||
if ((fr->fr_flags & RFTHREAD) != 0) { | if ((fr->fr_flags & RFTHREAD) != 0) { | ||||
mtx_lock(&ppeers_lock); | mtx_lock(&V_ppeers_lock); | ||||
p2->p_peers = p1->p_peers; | p2->p_peers = p1->p_peers; | ||||
p1->p_peers = p2; | p1->p_peers = p2; | ||||
p2->p_leader = p1->p_leader; | p2->p_leader = p1->p_leader; | ||||
mtx_unlock(&ppeers_lock); | mtx_unlock(&V_ppeers_lock); | ||||
PROC_LOCK(p1->p_leader); | PROC_LOCK(p1->p_leader); | ||||
if ((p1->p_leader->p_flag & P_WEXIT) != 0) { | if ((p1->p_leader->p_flag & P_WEXIT) != 0) { | ||||
PROC_UNLOCK(p1->p_leader); | PROC_UNLOCK(p1->p_leader); | ||||
/* | /* | ||||
* The task leader is exiting, so process p1 is | * The task leader is exiting, so process p1 is | ||||
* going to be killed shortly. Since p1 obviously | * going to be killed shortly. Since p1 obviously | ||||
* isn't dead yet, we know that the leader is either | * isn't dead yet, we know that the leader is either | ||||
* sending SIGKILL's to all the processes in this | * sending SIGKILL's to all the processes in this | ||||
Show All 10 Lines | if ((p1->p_leader->p_flag & P_WEXIT) != 0) { | ||||
PROC_UNLOCK(p2); | PROC_UNLOCK(p2); | ||||
} else | } else | ||||
PROC_UNLOCK(p1->p_leader); | PROC_UNLOCK(p1->p_leader); | ||||
} else { | } else { | ||||
p2->p_peers = NULL; | p2->p_peers = NULL; | ||||
p2->p_leader = p2; | p2->p_leader = p2; | ||||
} | } | ||||
sx_xlock(&proctree_lock); | sx_xlock(&V_proctree_lock); | ||||
PGRP_LOCK(p1->p_pgrp); | PGRP_LOCK(p1->p_pgrp); | ||||
PROC_LOCK(p2); | PROC_LOCK(p2); | ||||
PROC_LOCK(p1); | PROC_LOCK(p1); | ||||
/* | /* | ||||
* Preserve some more flags in subprocess. P_PROFIL has already | * Preserve some more flags in subprocess. P_PROFIL has already | ||||
* been preserved. | * been preserved. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if ((fr->fr_flags & RFNOWAIT) != 0) { | ||||
pptr = p1; | pptr = p1; | ||||
} | } | ||||
p2->p_pptr = pptr; | p2->p_pptr = pptr; | ||||
LIST_INSERT_HEAD(&pptr->p_children, p2, p_sibling); | LIST_INSERT_HEAD(&pptr->p_children, p2, p_sibling); | ||||
LIST_INIT(&p2->p_reaplist); | LIST_INIT(&p2->p_reaplist); | ||||
LIST_INSERT_HEAD(&p2->p_reaper->p_reaplist, p2, p_reapsibling); | LIST_INSERT_HEAD(&p2->p_reaper->p_reaplist, p2, p_reapsibling); | ||||
if (p2->p_reaper == p1) | if (p2->p_reaper == p1) | ||||
p2->p_reapsubtree = p2->p_pid; | p2->p_reapsubtree = p2->p_pid; | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&V_proctree_lock); | ||||
/* Inform accounting that we have forked. */ | /* Inform accounting that we have forked. */ | ||||
p2->p_acflag = AFORK; | p2->p_acflag = AFORK; | ||||
PROC_UNLOCK(p2); | PROC_UNLOCK(p2); | ||||
#ifdef KTRACE | #ifdef KTRACE | ||||
ktrprocfork(p1, p2); | ktrprocfork(p1, p2); | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
/* | /* | ||||
* Speculative check for PTRACE_FORK. PTRACE_FORK is not | * Speculative check for PTRACE_FORK. PTRACE_FORK is not | ||||
* synced with forks in progress so it is OK if we miss it | * synced with forks in progress so it is OK if we miss it | ||||
* if being set atm. | * if being set atm. | ||||
*/ | */ | ||||
if ((p1->p_ptevents & PTRACE_FORK) != 0) { | if ((p1->p_ptevents & PTRACE_FORK) != 0) { | ||||
sx_xlock(&proctree_lock); | sx_xlock(&V_proctree_lock); | ||||
PROC_LOCK(p2); | PROC_LOCK(p2); | ||||
/* | /* | ||||
* p1->p_ptevents & p1->p_pptr are protected by both | * p1->p_ptevents & p1->p_pptr are protected by both | ||||
* process and proctree locks for modifications, | * process and proctree locks for modifications, | ||||
* so owning proctree_lock allows the race-free read. | * so owning proctree_lock allows the race-free read. | ||||
*/ | */ | ||||
if ((p1->p_ptevents & PTRACE_FORK) != 0) { | if ((p1->p_ptevents & PTRACE_FORK) != 0) { | ||||
Show All 9 Lines | if ((p1->p_ptevents & PTRACE_FORK) != 0) { | ||||
td2->td_dbgflags |= TDB_STOPATFORK; | td2->td_dbgflags |= TDB_STOPATFORK; | ||||
proc_set_traced(p2, true); | proc_set_traced(p2, true); | ||||
CTR2(KTR_PTRACE, | CTR2(KTR_PTRACE, | ||||
"do_fork: attaching to new child pid %d: oppid %d", | "do_fork: attaching to new child pid %d: oppid %d", | ||||
p2->p_pid, p2->p_oppid); | p2->p_pid, p2->p_oppid); | ||||
proc_reparent(p2, p1->p_pptr); | proc_reparent(p2, p1->p_pptr); | ||||
} | } | ||||
PROC_UNLOCK(p2); | PROC_UNLOCK(p2); | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&V_proctree_lock); | ||||
} | } | ||||
if ((fr->fr_flags & RFSTOPPED) == 0) { | if ((fr->fr_flags & RFSTOPPED) == 0) { | ||||
/* | /* | ||||
* If RFSTOPPED not requested, make child runnable and | * If RFSTOPPED not requested, make child runnable and | ||||
* add to run queue. | * add to run queue. | ||||
*/ | */ | ||||
thread_lock(td2); | thread_lock(td2); | ||||
TD_SET_CAN_RUN(td2); | TD_SET_CAN_RUN(td2); | ||||
sched_add(td2, SRQ_BORING); | sched_add(td2, SRQ_BORING); | ||||
thread_unlock(td2); | thread_unlock(td2); | ||||
if (fr->fr_pidp != NULL) | if (fr->fr_pidp != NULL) | ||||
*fr->fr_pidp = p2->p_pid; | *fr->fr_pidp = p2->p_pid; | ||||
} else { | } else { | ||||
*fr->fr_procp = p2; | *fr->fr_procp = p2; | ||||
} | } | ||||
PROC_LOCK(p2); | PROC_LOCK(p2); | ||||
_PRELE(p2); | _PRELE(p2); | ||||
racct_proc_fork_done(p2); | racct_proc_fork_done(p2); | ||||
PROC_UNLOCK(p2); | PROC_UNLOCK(p2); | ||||
} | } | ||||
static VPS_DEFINE(int, curfail); | |||||
#define V_curfail VPS(curfail) | |||||
static VPS_DEFINE(struct timeval, lastfail); | |||||
#define V_lastfail VPS(lastfail) | |||||
int | int | ||||
fork1(struct thread *td, struct fork_req *fr) | fork1(struct thread *td, struct fork_req *fr) | ||||
{ | { | ||||
struct proc *p1, *newproc; | struct proc *p1, *newproc; | ||||
struct thread *td2; | struct thread *td2; | ||||
struct vmspace *vm2; | struct vmspace *vm2; | ||||
struct file *fp_procdesc; | struct file *fp_procdesc; | ||||
vm_ooffset_t mem_charged; | vm_ooffset_t mem_charged; | ||||
int error, nprocs_new, ok; | int error, nprocs_new, ok; | ||||
static int curfail; | |||||
static struct timeval lastfail; | |||||
int flags, pages; | int flags, pages; | ||||
flags = fr->fr_flags; | flags = fr->fr_flags; | ||||
pages = fr->fr_pages; | pages = fr->fr_pages; | ||||
if ((flags & RFSTOPPED) != 0) | if ((flags & RFSTOPPED) != 0) | ||||
MPASS(fr->fr_procp != NULL && fr->fr_pidp == NULL); | MPASS(fr->fr_procp != NULL && fr->fr_pidp == NULL); | ||||
else | else | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | fork1(struct thread *td, struct fork_req *fr) | ||||
* keep a global limit on the maximum number we will | * keep a global limit on the maximum number we will | ||||
* create. There are hard-limits as to the number of processes | * create. There are hard-limits as to the number of processes | ||||
* that can run, established by the KVA and memory usage for | * that can run, established by the KVA and memory usage for | ||||
* the process data. | * the process data. | ||||
* | * | ||||
* Don't allow a nonprivileged user to use the last ten | * Don't allow a nonprivileged user to use the last ten | ||||
* processes; don't let root exceed the limit. | * processes; don't let root exceed the limit. | ||||
*/ | */ | ||||
nprocs_new = atomic_fetchadd_int(&nprocs, 1) + 1; | nprocs_new = atomic_fetchadd_int(&V_nprocs, 1) + 1; | ||||
if ((nprocs_new >= maxproc - 10 && priv_check_cred(td->td_ucred, | if ((nprocs_new >= maxproc - 10 && priv_check_cred(td->td_ucred, | ||||
PRIV_MAXPROC, 0) != 0) || nprocs_new >= maxproc) { | PRIV_MAXPROC, 0) != 0) || nprocs_new >= maxproc) { | ||||
error = EAGAIN; | error = EAGAIN; | ||||
sx_xlock(&allproc_lock); | sx_xlock(&V_allproc_lock); | ||||
if (ppsratecheck(&lastfail, &curfail, 1)) { | if (ppsratecheck(&V_lastfail, &V_curfail, 1)) { | ||||
printf("maxproc limit exceeded by uid %u (pid %d); " | printf("maxproc limit exceeded by uid %u (pid %d); " | ||||
"see tuning(7) and login.conf(5)\n", | "see tuning(7) and login.conf(5)\n", | ||||
td->td_ucred->cr_ruid, p1->p_pid); | td->td_ucred->cr_ruid, p1->p_pid); | ||||
} | } | ||||
sx_xunlock(&allproc_lock); | sx_xunlock(&V_allproc_lock); | ||||
goto fail2; | goto fail2; | ||||
} | } | ||||
/* | /* | ||||
* If required, create a process descriptor in the parent first; we | * If required, create a process descriptor in the parent first; we | ||||
* will abandon it if something goes wrong. We don't finit() until | * will abandon it if something goes wrong. We don't finit() until | ||||
* later. | * later. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
#ifdef MAC | #ifdef MAC | ||||
mac_proc_init(newproc); | mac_proc_init(newproc); | ||||
#endif | #endif | ||||
newproc->p_klist = knlist_alloc(&newproc->p_mtx); | newproc->p_klist = knlist_alloc(&newproc->p_mtx); | ||||
STAILQ_INIT(&newproc->p_ktr); | STAILQ_INIT(&newproc->p_ktr); | ||||
/* We have to lock the process tree while we look for a pid. */ | /* We have to lock the process tree while we look for a pid. */ | ||||
sx_xlock(&proctree_lock); | sx_xlock(&V_proctree_lock); | ||||
sx_xlock(&allproc_lock); | sx_xlock(&V_allproc_lock); | ||||
/* | /* | ||||
* Increment the count of procs running with this uid. Don't allow | * Increment the count of procs running with this uid. Don't allow | ||||
* a nonprivileged user to exceed their current limit. | * a nonprivileged user to exceed their current limit. | ||||
* | * | ||||
* XXXRW: Can we avoid privilege here if it's not needed? | * XXXRW: Can we avoid privilege here if it's not needed? | ||||
*/ | */ | ||||
error = priv_check_cred(td->td_ucred, PRIV_PROC_LIMIT, 0); | error = priv_check_cred(td->td_ucred, PRIV_PROC_LIMIT, 0); | ||||
if (error == 0) | if (error == 0) | ||||
ok = chgproccnt(td->td_ucred->cr_ruidinfo, 1, 0); | ok = chgproccnt(td->td_ucred->cr_ruidinfo, 1, 0); | ||||
else { | else { | ||||
ok = chgproccnt(td->td_ucred->cr_ruidinfo, 1, | ok = chgproccnt(td->td_ucred->cr_ruidinfo, 1, | ||||
lim_cur(td, RLIMIT_NPROC)); | lim_cur(td, RLIMIT_NPROC)); | ||||
} | } | ||||
if (ok) { | if (ok) { | ||||
do_fork(td, fr, newproc, td2, vm2, fp_procdesc); | do_fork(td, fr, newproc, td2, vm2, fp_procdesc); | ||||
return (0); | return (0); | ||||
} | } | ||||
error = EAGAIN; | error = EAGAIN; | ||||
sx_xunlock(&allproc_lock); | sx_xunlock(&V_allproc_lock); | ||||
sx_xunlock(&proctree_lock); | sx_xunlock(&V_proctree_lock); | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_proc_destroy(newproc); | mac_proc_destroy(newproc); | ||||
#endif | #endif | ||||
racct_proc_exit(newproc); | racct_proc_exit(newproc); | ||||
fail1: | fail1: | ||||
crfree(newproc->p_ucred); | crfree(newproc->p_ucred); | ||||
newproc->p_ucred = NULL; | newproc->p_ucred = NULL; | ||||
fail2: | fail2: | ||||
if (vm2 != NULL) | if (vm2 != NULL) | ||||
vmspace_free(vm2); | vmspace_free(vm2); | ||||
uma_zfree(proc_zone, newproc); | uma_zfree(proc_zone, newproc); | ||||
if ((flags & RFPROCDESC) != 0 && fp_procdesc != NULL) { | if ((flags & RFPROCDESC) != 0 && fp_procdesc != NULL) { | ||||
fdclose(td, fp_procdesc, *fr->fr_pd_fd); | fdclose(td, fp_procdesc, *fr->fr_pd_fd); | ||||
fdrop(fp_procdesc, td); | fdrop(fp_procdesc, td); | ||||
} | } | ||||
atomic_add_int(&nprocs, -1); | atomic_add_int(&V_nprocs, -1); | ||||
pause("fork", hz / 2); | pause("fork", hz / 2); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Handle the return of a child process from fork1(). This function | * Handle the return of a child process from fork1(). This function | ||||
* is called from the MD fork_trampoline() entry point. | * is called from the MD fork_trampoline() entry point. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 101 Lines • Show Last 20 Lines |