static int setpmask(caddr_t data) { STRUCT_DECL(auditpinfo, apinfo); proc_t *proc; cred_t *newcred; auditinfo_addr_t *ainfo; struct p_audit_data *pad; model_t model; model = get_udatamodel(); STRUCT_INIT(apinfo, model); if (copyin(data, STRUCT_BUF(apinfo), STRUCT_SIZE(apinfo))) return (EFAULT); mutex_enter(&pidlock); if ((proc = prfind(STRUCT_FGET(apinfo, ap_pid))) == NULL) { mutex_exit(&pidlock); return (ESRCH); } mutex_enter(&proc->p_lock); /* so process doesn't go away */ mutex_exit(&pidlock); newcred = cralloc(); if ((ainfo = crgetauinfo_modifiable(newcred)) == NULL) { mutex_exit(&proc->p_lock); crfree(newcred); return (EINVAL); } mutex_enter(&proc->p_crlock); crcopy_to(proc->p_cred, newcred); proc->p_cred = newcred; ainfo->ai_mask = STRUCT_FGET(apinfo, ap_mask); /* * Unlock. No need to broadcast changes via set_proc_pre_sys(), * since t_pre_sys is ALWAYS on when audit is enabled... due to * syscall auditing. */ crfree(newcred); mutex_exit(&proc->p_crlock); /* Reset flag for any previous pending mask change; this supercedes */ pad = P2A(proc); ASSERT(pad != NULL); mutex_enter(&(pad->pad_lock)); pad->pad_flags &= ~PAD_SETMASK; mutex_exit(&(pad->pad_lock)); mutex_exit(&proc->p_lock); return (0); }
/* * Bind the lwp:id of process:pid to processor set: pset */ static int pset_bind_lwp(psetid_t pset, id_t id, pid_t pid, psetid_t *opset) { kthread_t *tp; proc_t *pp; psetid_t oldpset; void *projbuf, *zonebuf; int error = 0; pool_lock(); mutex_enter(&cpu_lock); projbuf = fss_allocbuf(FSS_NPROJ_BUF, FSS_ALLOC_PROJ); zonebuf = fss_allocbuf(FSS_NPROJ_BUF, FSS_ALLOC_ZONE); mutex_enter(&pidlock); if ((pid == P_MYID && id == P_MYID) || (pid == curproc->p_pid && id == P_MYID)) { pp = curproc; tp = curthread; mutex_enter(&pp->p_lock); } else { if (pid == P_MYID) { pp = curproc; } else if ((pp = prfind(pid)) == NULL) { error = ESRCH; goto err; } if (pp != curproc && id == P_MYID) { error = EINVAL; goto err; } mutex_enter(&pp->p_lock); if ((tp = idtot(pp, id)) == NULL) { mutex_exit(&pp->p_lock); error = ESRCH; goto err; } } error = pset_bind_thread(tp, pset, &oldpset, projbuf, zonebuf); mutex_exit(&pp->p_lock); err: mutex_exit(&pidlock); fss_freebuf(projbuf, FSS_ALLOC_PROJ); fss_freebuf(zonebuf, FSS_ALLOC_ZONE); mutex_exit(&cpu_lock); pool_unlock(); if (opset != NULL) { if (copyout(&oldpset, opset, sizeof (psetid_t)) != 0) return (set_errno(EFAULT)); } if (error != 0) return (set_errno(error)); return (0); }
static int putacct(idtype_t idtype, id_t id, void *buf, size_t bufsize, int flags) { int error; taskid_t tkid; proc_t *p; task_t *tk; void *kbuf; struct exacct_globals *acg; if (bufsize == 0 || bufsize > EXACCT_MAX_BUFSIZE) return (set_errno(EINVAL)); kbuf = kmem_alloc(bufsize, KM_SLEEP); if (copyin(buf, kbuf, bufsize) != 0) { error = EFAULT; goto out; } acg = zone_getspecific(exacct_zone_key, curproc->p_zone); switch (idtype) { case P_PID: mutex_enter(&pidlock); if ((p = prfind(id)) == NULL) { mutex_exit(&pidlock); error = ESRCH; } else { zone_t *zone = p->p_zone; tkid = p->p_task->tk_tkid; zone_hold(zone); mutex_exit(&pidlock); error = exacct_tag_proc(&acg->ac_proc, id, tkid, kbuf, bufsize, flags, zone->zone_nodename); zone_rele(zone); } break; case P_TASKID: if ((tk = task_hold_by_id(id)) != NULL) { error = exacct_tag_task(&acg->ac_task, tk, kbuf, bufsize, flags); task_rele(tk); } else { error = ESRCH; } break; default: error = EINVAL; break; } out: kmem_free(kbuf, bufsize); return (error == 0 ? error : set_errno(error)); }
/* ARGSUSED */ int sys_setpgid(struct proc *curp, void *v, register_t *retval) { struct sys_setpgid_args /* { syscallarg(pid_t) pid; syscallarg(int) pgid; } */ *uap = v; struct process *curpr = curp->p_p; struct process *targpr; /* target process */ struct pgrp *pgrp, *newpgrp; /* target pgrp */ pid_t pid; int pgid, error; pid = SCARG(uap, pid); pgid = SCARG(uap, pgid); if (pgid < 0) return (EINVAL); newpgrp = pool_get(&pgrp_pool, PR_WAITOK); if (pid != 0 && pid != curpr->ps_pid) { if ((targpr = prfind(pid)) == 0 || !inferior(targpr, curpr)) { error = ESRCH; goto out; } if (targpr->ps_session != curpr->ps_session) { error = EPERM; goto out; } if (targpr->ps_flags & PS_EXEC) { error = EACCES; goto out; } } else targpr = curpr; if (SESS_LEADER(targpr)) { error = EPERM; goto out; } if (pgid == 0) pgid = targpr->ps_pid; else if (pgid != targpr->ps_pid) if ((pgrp = pgfind(pgid)) == 0 || pgrp->pg_session != curpr->ps_session) { error = EPERM; goto out; } return (enterpgrp(targpr, pgid, newpgrp, NULL)); out: pool_put(&pgrp_pool, newpgrp); return (error); }
/* * lxpr_lock(): * * Lookup process from pid and return with p_plock and P_PR_LOCK held. */ proc_t * lxpr_lock(pid_t pid) { proc_t *p; kmutex_t *mp; ASSERT(!MUTEX_HELD(&pidlock)); for (;;) { mutex_enter(&pidlock); /* * If the pid is 1, we really want the zone's init process */ p = prfind((pid == 1) ? curproc->p_zone->zone_proc_initpid : pid); if (p == NULL || p->p_stat == SIDL) { mutex_exit(&pidlock); return (NULL); } /* * p_lock is persistent, but p itself is not -- it could * vanish during cv_wait(). Load p->p_lock now so we can * drop it after cv_wait() without referencing p. */ mp = &p->p_lock; mutex_enter(mp); mutex_exit(&pidlock); if (p->p_flag & SEXITING) { /* * This process is exiting -- let it go. */ mutex_exit(mp); return (NULL); } if (!(p->p_proc_flag & P_PR_LOCK)) break; cv_wait(&pr_pid_cv[p->p_slot], mp); mutex_exit(mp); } p->p_proc_flag |= P_PR_LOCK; THREAD_KPRI_REQUEST(); return (p); }
static int sigqkill(pid_t pid, sigsend_t *sigsend) { proc_t *p; int error; if ((uint_t)sigsend->sig >= NSIG) return (EINVAL); if (pid == -1) { procset_t set; setprocset(&set, POP_AND, P_ALL, P_MYID, P_ALL, P_MYID); error = sigsendset(&set, sigsend); } else if (pid > 0) { mutex_enter(&pidlock); if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) error = ESRCH; else { error = sigsendproc(p, sigsend); if (error == 0 && sigsend->perm == 0) error = EPERM; } mutex_exit(&pidlock); } else { int nfound = 0; pid_t pgid; if (pid == 0) pgid = ttoproc(curthread)->p_pgrp; else pgid = -pid; error = 0; mutex_enter(&pidlock); for (p = pgfind(pgid); p && !error; p = p->p_pglink) { if (p->p_stat != SIDL) { nfound++; error = sigsendproc(p, sigsend); } } mutex_exit(&pidlock); if (nfound == 0) error = ESRCH; else if (error == 0 && sigsend->perm == 0) error = EPERM; } return (error); }
static void xen_shutdown(void *arg) { int cmd = (uintptr_t)arg; proc_t *initpp; ASSERT(cmd > SHUTDOWN_INVALID && cmd < SHUTDOWN_MAX); if (cmd == SHUTDOWN_SUSPEND) { xen_suspend_domain(); return; } switch (cmd) { case SHUTDOWN_POWEROFF: force_shutdown_method = AD_POWEROFF; break; case SHUTDOWN_HALT: force_shutdown_method = AD_HALT; break; case SHUTDOWN_REBOOT: force_shutdown_method = AD_BOOT; break; } /* * If we're still booting and init(1) isn't set up yet, simply halt. */ mutex_enter(&pidlock); initpp = prfind(P_INITPID); mutex_exit(&pidlock); if (initpp == NULL) { extern void halt(char *); halt("Power off the System"); /* just in case */ } /* * else, graceful shutdown with inittab and all getting involved */ psignal(initpp, SIGPWR); (void) timeout(xen_dirty_shutdown, arg, SHUTDOWN_TIMEOUT_SECS * drv_usectohz(MICROSEC)); }
/* * SysVR.4 compatible getpgid() */ pid_t sys_getpgid(struct proc *curp, void *v, register_t *retval) { struct sys_getpgid_args /* { syscallarg(pid_t) pid; } */ *uap = v; struct process *targpr = curp->p_p; if (SCARG(uap, pid) == 0 || SCARG(uap, pid) == targpr->ps_pid) goto found; if ((targpr = prfind(SCARG(uap, pid))) == NULL) return (ESRCH); if (targpr->ps_session != curp->p_p->ps_session) return (EPERM); found: *retval = targpr->ps_pgid; return (0); }
static int getacct_proc(ac_info_t *ac_proc, pid_t pid, void *buf, size_t bufsize, size_t *sizep) { proc_t *p; proc_usage_t *pu; ulong_t mask[AC_MASK_SZ]; ulong_t *ac_mask = &mask[0]; int error; mutex_enter(&ac_proc->ac_lock); if (ac_proc->ac_state == AC_OFF) { mutex_exit(&ac_proc->ac_lock); return (ENOTACTIVE); } bt_copy(&ac_proc->ac_mask[0], ac_mask, AC_MASK_SZ); mutex_exit(&ac_proc->ac_lock); pu = kmem_zalloc(sizeof (proc_usage_t), KM_SLEEP); pu->pu_command = kmem_zalloc(MAXCOMLEN + 1, KM_SLEEP); mutex_enter(&pidlock); if ((p = prfind(pid)) == NULL) { mutex_exit(&pidlock); kmem_free(pu->pu_command, MAXCOMLEN + 1); kmem_free(pu, sizeof (proc_usage_t)); return (ESRCH); } mutex_enter(&p->p_lock); mutex_exit(&pidlock); exacct_calculate_proc_usage(p, pu, ac_mask, EW_PARTIAL, 0); mutex_exit(&p->p_lock); error = exacct_assemble_proc_usage(ac_proc, pu, getacct_callback, buf, bufsize, sizep, EW_PARTIAL); kmem_free(pu->pu_command, MAXCOMLEN + 1); kmem_free(pu, sizeof (proc_usage_t)); return (error); }
static int pset_bind(psetid_t pset, idtype_t idtype, id_t id, psetid_t *opset) { kthread_t *tp; proc_t *pp; task_t *tk; kproject_t *kpj; contract_t *ct; zone_t *zptr; psetid_t oldpset; int error = 0; void *projbuf, *zonebuf; pool_lock(); if ((pset != PS_QUERY) && (pset != PS_SOFT) && (pset != PS_HARD) && (pset != PS_QUERY_TYPE)) { /* * Check if the set actually exists before checking * permissions. This is the historical error * precedence. Note that if pset was PS_MYID, the * cpupart_get_cpus call will change it to the * processor set id of the caller (or PS_NONE if the * caller is not bound to a processor set). */ if (pool_state == POOL_ENABLED) { pool_unlock(); return (set_errno(ENOTSUP)); } if (cpupart_get_cpus(&pset, NULL, NULL) != 0) { pool_unlock(); return (set_errno(EINVAL)); } else if (pset != PS_NONE && secpolicy_pset(CRED()) != 0) { pool_unlock(); return (set_errno(EPERM)); } } /* * Pre-allocate enough buffers for FSS for all active projects * and for all active zones on the system. Unused buffers will * be freed later by fss_freebuf(). */ mutex_enter(&cpu_lock); projbuf = fss_allocbuf(FSS_NPROJ_BUF, FSS_ALLOC_PROJ); zonebuf = fss_allocbuf(FSS_NPROJ_BUF, FSS_ALLOC_ZONE); switch (idtype) { case P_LWPID: pp = curproc; mutex_enter(&pidlock); mutex_enter(&pp->p_lock); if (id == P_MYID) { tp = curthread; } else { if ((tp = idtot(pp, id)) == NULL) { mutex_exit(&pp->p_lock); mutex_exit(&pidlock); error = ESRCH; break; } } error = pset_bind_thread(tp, pset, &oldpset, projbuf, zonebuf); mutex_exit(&pp->p_lock); mutex_exit(&pidlock); break; case P_PID: mutex_enter(&pidlock); if (id == P_MYID) { pp = curproc; } else if ((pp = prfind(id)) == NULL) { mutex_exit(&pidlock); error = ESRCH; break; } error = pset_bind_process(pp, pset, &oldpset, projbuf, zonebuf); mutex_exit(&pidlock); break; case P_TASKID: mutex_enter(&pidlock); if (id == P_MYID) id = curproc->p_task->tk_tkid; if ((tk = task_hold_by_id(id)) == NULL) { mutex_exit(&pidlock); error = ESRCH; break; } error = pset_bind_task(tk, pset, &oldpset, projbuf, zonebuf); mutex_exit(&pidlock); task_rele(tk); break; case P_PROJID: pp = curproc; if (id == P_MYID) id = curprojid(); if ((kpj = project_hold_by_id(id, pp->p_zone, PROJECT_HOLD_FIND)) == NULL) { error = ESRCH; break; } mutex_enter(&pidlock); error = pset_bind_project(kpj, pset, &oldpset, projbuf, zonebuf); mutex_exit(&pidlock); project_rele(kpj); break; case P_ZONEID: if (id == P_MYID) id = getzoneid(); if ((zptr = zone_find_by_id(id)) == NULL) { error = ESRCH; break; } mutex_enter(&pidlock); error = pset_bind_zone(zptr, pset, &oldpset, projbuf, zonebuf); mutex_exit(&pidlock); zone_rele(zptr); break; case P_CTID: if (id == P_MYID) id = PRCTID(curproc); if ((ct = contract_type_ptr(process_type, id, curproc->p_zone->zone_uniqid)) == NULL) { error = ESRCH; break; } mutex_enter(&pidlock); error = pset_bind_contract(ct->ct_data, pset, &oldpset, projbuf, zonebuf); mutex_exit(&pidlock); contract_rele(ct); break; case P_PSETID: if (id == P_MYID || pset != PS_NONE || !INGLOBALZONE(curproc)) { error = EINVAL; break; } error = pset_unbind(id, projbuf, zonebuf, idtype); break; case P_ALL: if (id == P_MYID || pset != PS_NONE || !INGLOBALZONE(curproc)) { error = EINVAL; break; } error = pset_unbind(PS_NONE, projbuf, zonebuf, idtype); break; default: error = EINVAL; break; } fss_freebuf(projbuf, FSS_ALLOC_PROJ); fss_freebuf(zonebuf, FSS_ALLOC_ZONE); mutex_exit(&cpu_lock); pool_unlock(); if (error != 0) return (set_errno(error)); if (opset != NULL) { if (copyout(&oldpset, opset, sizeof (psetid_t)) != 0) return (set_errno(EFAULT)); } return (0); }
int corectl(int subcode, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) { int error = 0; proc_t *p; refstr_t *rp; size_t size; char *path; core_content_t content = CC_CONTENT_INVALID; struct core_globals *cg; zone_t *zone = curproc->p_zone; cg = zone_getspecific(core_zone_key, zone); ASSERT(cg != NULL); switch (subcode) { case CC_SET_OPTIONS: if ((error = secpolicy_coreadm(CRED())) == 0) { if (arg1 & ~CC_OPTIONS) error = EINVAL; else cg->core_options = (uint32_t)arg1; } break; case CC_GET_OPTIONS: return (cg->core_options); case CC_GET_GLOBAL_PATH: case CC_GET_DEFAULT_PATH: case CC_GET_PROCESS_PATH: if (subcode == CC_GET_GLOBAL_PATH) { mutex_enter(&cg->core_lock); if ((rp = cg->core_file) != NULL) refstr_hold(rp); mutex_exit(&cg->core_lock); } else if (subcode == CC_GET_DEFAULT_PATH) { rp = corectl_path_value(cg->core_default_path); } else { rp = NULL; mutex_enter(&pidlock); if ((p = prfind((pid_t)arg3)) == NULL || p->p_stat == SIDL) { mutex_exit(&pidlock); error = ESRCH; } else { mutex_enter(&p->p_lock); mutex_exit(&pidlock); mutex_enter(&p->p_crlock); if (!hasprocperm(p->p_cred, CRED())) error = EPERM; else if (p->p_corefile != NULL) rp = corectl_path_value(p->p_corefile); mutex_exit(&p->p_crlock); mutex_exit(&p->p_lock); } } if (rp == NULL) { if (error == 0 && suword8((void *)arg1, 0)) error = EFAULT; } else { error = copyoutstr(refstr_value(rp), (char *)arg1, (size_t)arg2, NULL); refstr_rele(rp); } break; case CC_SET_GLOBAL_PATH: case CC_SET_DEFAULT_PATH: if ((error = secpolicy_coreadm(CRED())) != 0) break; /* FALLTHROUGH */ case CC_SET_PROCESS_PATH: if ((size = MIN((size_t)arg2, MAXPATHLEN)) == 0) { error = EINVAL; break; } path = kmem_alloc(size, KM_SLEEP); error = copyinstr((char *)arg1, path, size, NULL); if (error == 0) { if (subcode == CC_SET_PROCESS_PATH) { error = set_proc_info((pid_t)arg3, path, 0); } else if (subcode == CC_SET_DEFAULT_PATH) { corectl_path_set(cg->core_default_path, path); } else if (*path != '\0' && *path != '/') { error = EINVAL; } else { refstr_t *nrp = refstr_alloc(path); mutex_enter(&cg->core_lock); rp = cg->core_file; if (*path == '\0') cg->core_file = NULL; else refstr_hold(cg->core_file = nrp); mutex_exit(&cg->core_lock); if (rp != NULL) refstr_rele(rp); refstr_rele(nrp); } } kmem_free(path, size); break; case CC_SET_GLOBAL_CONTENT: case CC_SET_DEFAULT_CONTENT: if ((error = secpolicy_coreadm(CRED())) != 0) break; /* FALLTHROUGH */ case CC_SET_PROCESS_CONTENT: error = copyin((void *)arg1, &content, sizeof (content)); if (error != 0) break; /* * If any unknown bits are set, don't let this charade * continue. */ if (content & ~CC_CONTENT_ALL) { error = EINVAL; break; } if (subcode == CC_SET_PROCESS_CONTENT) { error = set_proc_info((pid_t)arg2, NULL, content); } else if (subcode == CC_SET_DEFAULT_CONTENT) { corectl_content_set(cg->core_default_content, content); } else { mutex_enter(&cg->core_lock); cg->core_content = content; mutex_exit(&cg->core_lock); } break; case CC_GET_GLOBAL_CONTENT: content = cg->core_content; error = copyout(&content, (void *)arg1, sizeof (content)); break; case CC_GET_DEFAULT_CONTENT: content = corectl_content_value(cg->core_default_content); error = copyout(&content, (void *)arg1, sizeof (content)); break; case CC_GET_PROCESS_CONTENT: mutex_enter(&pidlock); if ((p = prfind((pid_t)arg2)) == NULL || p->p_stat == SIDL) { mutex_exit(&pidlock); error = ESRCH; break; } mutex_enter(&p->p_lock); mutex_exit(&pidlock); mutex_enter(&p->p_crlock); if (!hasprocperm(p->p_cred, CRED())) error = EPERM; else if (p->p_content == NULL) content = CC_CONTENT_NONE; else content = corectl_content_value(p->p_content); mutex_exit(&p->p_crlock); mutex_exit(&p->p_lock); if (error == 0) error = copyout(&content, (void *)arg1, sizeof (content)); break; default: error = EINVAL; break; } if (error) return (set_errno(error)); return (0); }
/* * The dotoprocs function locates the process(es) specified * by the procset structure pointed to by psp. funcp points to a * function which dotoprocs will call for each process in the * specified set. The arguments to this function will be a pointer * to the current process from the set and arg. * If the called function returns -1, it means that processing of the * procset should stop and a normal (non-error) return should be made * to the caller of dotoprocs. * If the called function returns any other non-zero value the search * is terminated and the function's return value is returned to * the caller of dotoprocs. This will normally be an error code. * Otherwise, dotoprocs will return zero after processing the entire * process set unless no processes were found in which case ESRCH will * be returned. */ int dotoprocs(procset_t *psp, int (*funcp)(), char *arg) { proc_t *prp; /* A process from the set */ int error; int nfound; /* Nbr of processes found. */ proc_t *lastprp; /* Last proc found. */ ASSERT(funcp != NULL); /* * Check that the procset_t is valid. */ error = checkprocset(psp); if (error) { return (error); } /* * Check for the special value P_MYID in either operand * and replace it with the correct value. We don't check * for an error return from getmyid() because the idtypes * have been validated by the checkprocset() call above. */ mutex_enter(&pidlock); if (psp->p_lid == P_MYID) { psp->p_lid = getmyid(psp->p_lidtype); } if (psp->p_rid == P_MYID) { psp->p_rid = getmyid(psp->p_ridtype); } /* * If psp only acts on a single proc, we can reduce pidlock hold time * by avoiding a needless scan of the entire proc list. Although * there are many procset_t combinations which might boil down to a * single proc, the most common case is an AND operation where one * side is a specific pid, and the other side is P_ALL, so that is * the case for which we will provide a fast path. Other cases could * be added in a similar fashion if they were to become significant * pidlock bottlenecks. * * Perform the check symmetrically: either the left or right side may * specify a pid, with the opposite side being 'all'. */ if (psp->p_op == POP_AND) { if (((psp->p_lidtype == P_PID) && (psp->p_ridtype == P_ALL)) || ((psp->p_ridtype == P_PID) && (psp->p_lidtype == P_ALL))) { id_t pid; pid = (psp->p_lidtype == P_PID) ? psp->p_lid : psp->p_rid; if (((prp = prfind((pid_t)pid)) == NULL) || (prp->p_stat == SIDL || prp->p_stat == SZOMB || prp->p_tlist == NULL || prp->p_flag & SSYS)) { /* * Specified proc doesn't exist or should * not be operated on. * Don't need to make HASZONEACCESS check * here since prfind() takes care of that. */ mutex_exit(&pidlock); return (ESRCH); } /* * Operate only on the specified proc. It's okay * if it's init. */ error = (*funcp)(prp, arg); mutex_exit(&pidlock); if (error == -1) error = 0; return (error); } } nfound = 0; error = 0; for (prp = practive; prp != NULL; prp = prp->p_next) { /* * If caller is in a non-global zone, skip processes * in other zones. */ if (!HASZONEACCESS(curproc, prp->p_zone->zone_id)) continue; /* * Ignore this process if it's coming or going, * if it's a system process or if it's not in * the given procset_t. */ if (prp->p_stat == SIDL || prp->p_stat == SZOMB) continue; mutex_enter(&prp->p_lock); if (prp->p_flag & SSYS || prp->p_tlist == NULL || procinset(prp, psp) == 0) { mutex_exit(&prp->p_lock); } else { mutex_exit(&prp->p_lock); nfound++; lastprp = prp; if (prp != proc_init) { error = (*funcp)(prp, arg); if (error == -1) { mutex_exit(&pidlock); return (0); } else if (error) { mutex_exit(&pidlock); return (error); } } } } if (nfound == 0) { mutex_exit(&pidlock); return (ESRCH); } if (nfound == 1 && lastprp == proc_init) error = (*funcp)(lastprp, arg); if (error == -1) error = 0; mutex_exit(&pidlock); return (error); }
int klpd_unreg(int did, idtype_t type, id_t id) { door_handle_t dh; int res = 0; proc_t *p; pid_t pid; projid_t proj; kproject_t *kpp = NULL; credklpd_t *ckp; switch (type) { case P_PID: pid = (pid_t)id; break; case P_PROJID: proj = (projid_t)id; kpp = project_hold_by_id(proj, crgetzone(CRED()), PROJECT_HOLD_FIND); if (kpp == NULL) return (set_errno(ESRCH)); break; default: return (set_errno(ENOTSUP)); } dh = door_ki_lookup(did); if (dh == NULL) { if (kpp != NULL) project_rele(kpp); return (set_errno(EINVAL)); } if (kpp != NULL) { mutex_enter(&klpd_mutex); if (kpp->kpj_klpd == NULL) res = ESRCH; else klpd_freelist(&kpp->kpj_klpd); mutex_exit(&klpd_mutex); project_rele(kpp); goto out; } else if ((int)pid > 0) { mutex_enter(&pidlock); p = prfind(pid); if (p == NULL) { mutex_exit(&pidlock); door_ki_rele(dh); return (set_errno(ESRCH)); } mutex_enter(&p->p_crlock); mutex_exit(&pidlock); } else if (pid == 0) { p = curproc; mutex_enter(&p->p_crlock); } else { res = klpd_unreg_dh(dh); goto out; } ckp = crgetcrklpd(p->p_cred); if (ckp != NULL) { crklpd_setreg(ckp, NULL); } else { res = ESRCH; } mutex_exit(&p->p_crlock); out: door_ki_rele(dh); if (res != 0) return (set_errno(res)); return (0); }
/* * Register the klpd. * If the pid_t passed in is positive, update the registration for * the specific process; that is only possible if the process already * has a registration on it. This change of registration will affect * all processes which share common ancestry. * * MY_PID (pid 0) can be used to create or change the context for * the current process, typically done after fork(). * * A negative value can be used to register a klpd globally. * * The per-credential klpd needs to be cleaned up when entering * a zone or unsetting the flag. */ int klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf) { cred_t *cr = CRED(); door_handle_t dh; klpd_reg_t *kpd; priv_set_t pset; door_info_t di; credklpd_t *ckp = NULL; pid_t pid = -1; projid_t proj = -1; kproject_t *kpp = NULL; if (CR_FLAGS(cr) & PRIV_XPOLICY) return (set_errno(EINVAL)); if (copyin(psetbuf, &pset, sizeof (priv_set_t))) return (set_errno(EFAULT)); if (!priv_issubset(&pset, &CR_OEPRIV(cr))) return (set_errno(EPERM)); switch (type) { case P_PID: pid = (pid_t)id; if (pid == P_MYPID) pid = curproc->p_pid; if (pid == curproc->p_pid) ckp = crklpd_alloc(); break; case P_PROJID: proj = (projid_t)id; kpp = project_hold_by_id(proj, crgetzone(cr), PROJECT_HOLD_FIND); if (kpp == NULL) return (set_errno(ESRCH)); break; default: return (set_errno(ENOTSUP)); } /* * Verify the door passed in; it must be a door and we won't * allow processes to be called on their own behalf. */ dh = door_ki_lookup(did); if (dh == NULL || door_ki_info(dh, &di) != 0) { if (ckp != NULL) crklpd_rele(ckp); if (kpp != NULL) project_rele(kpp); return (set_errno(EBADF)); } if (type == P_PID && pid == di.di_target) { if (ckp != NULL) crklpd_rele(ckp); ASSERT(kpp == NULL); return (set_errno(EINVAL)); } kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP); crhold(kpd->klpd_cred = cr); kpd->klpd_door = dh; kpd->klpd_door_pid = di.di_target; kpd->klpd_ref = 1; kpd->klpd_pset = pset; if (kpp != NULL) { mutex_enter(&klpd_mutex); kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE); mutex_exit(&klpd_mutex); if (kpd != NULL) klpd_rele(kpd); project_rele(kpp); } else if ((int)pid < 0) { /* Global daemon */ mutex_enter(&klpd_mutex); (void) klpd_link(kpd, &klpd_list, B_FALSE); mutex_exit(&klpd_mutex); } else if (pid == curproc->p_pid) { proc_t *p = curproc; cred_t *newcr = cralloc(); /* No need to lock, sole reference to ckp */ kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE); if (kpd != NULL) klpd_rele(kpd); mutex_enter(&p->p_crlock); cr = p->p_cred; crdup_to(cr, newcr); crsetcrklpd(newcr, ckp); p->p_cred = newcr; /* Already held for p_cred */ crhold(newcr); /* Hold once for the current thread */ mutex_exit(&p->p_crlock); crfree(cr); /* One for the p_cred */ crset(p, newcr); } else { proc_t *p; cred_t *pcr; mutex_enter(&pidlock); p = prfind(pid); if (p == NULL || !prochasprocperm(p, curproc, CRED())) { mutex_exit(&pidlock); klpd_rele(kpd); return (set_errno(p == NULL ? ESRCH : EPERM)); } mutex_enter(&p->p_crlock); crhold(pcr = p->p_cred); mutex_exit(&pidlock); mutex_exit(&p->p_crlock); /* * We're going to update the credential's ckp in place; * this requires that it exists. */ ckp = crgetcrklpd(pcr); if (ckp == NULL) { crfree(pcr); klpd_rele(kpd); return (set_errno(EINVAL)); } crklpd_setreg(ckp, kpd); crfree(pcr); } return (0); }
static int set_proc_info(pid_t pid, const char *path, core_content_t content) { proc_t *p; counter_t counter; int error = 0; counter.cc_count = 0; /* * Only one of the core file path or content can be set at a time. */ if (path != NULL) { counter.cc_path = corectl_path_alloc(path); counter.cc_content = NULL; } else { counter.cc_path = NULL; counter.cc_content = corectl_content_alloc(content); } if (pid == -1) { procset_t set; setprocset(&set, POP_AND, P_ALL, P_MYID, P_ALL, P_MYID); error = dotoprocs(&set, set_one_proc_info, (char *)&counter); if (error == 0 && counter.cc_count == 0) error = EPERM; } else if (pid > 0) { mutex_enter(&pidlock); if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) { error = ESRCH; } else { (void) set_one_proc_info(p, &counter); if (counter.cc_count == 0) error = EPERM; } mutex_exit(&pidlock); } else { int nfound = 0; pid_t pgid; if (pid == 0) pgid = curproc->p_pgrp; else pgid = -pid; mutex_enter(&pidlock); for (p = pgfind(pgid); p != NULL; p = p->p_pglink) { if (p->p_stat != SIDL) { nfound++; (void) set_one_proc_info(p, &counter); } } mutex_exit(&pidlock); if (nfound == 0) error = ESRCH; else if (counter.cc_count == 0) error = EPERM; } if (path != NULL) corectl_path_rele(counter.cc_path); else corectl_content_rele(counter.cc_content); if (error) return (set_errno(error)); return (0); }
static int getpinfo_addr(caddr_t data, int len) { STRUCT_DECL(auditpinfo_addr, apinfo); proc_t *proc; const auditinfo_addr_t *ainfo; model_t model; cred_t *cr, *newcred; model = get_udatamodel(); STRUCT_INIT(apinfo, model); if (len < STRUCT_SIZE(apinfo)) return (EOVERFLOW); if (copyin(data, STRUCT_BUF(apinfo), STRUCT_SIZE(apinfo))) return (EFAULT); newcred = cralloc(); mutex_enter(&pidlock); if ((proc = prfind(STRUCT_FGET(apinfo, ap_pid))) == NULL) { mutex_exit(&pidlock); crfree(newcred); return (ESRCH); } mutex_enter(&proc->p_lock); /* so process doesn't go away */ mutex_exit(&pidlock); audit_update_context(proc, newcred); /* make sure it's up-to-date */ mutex_enter(&proc->p_crlock); crhold(cr = proc->p_cred); mutex_exit(&proc->p_crlock); mutex_exit(&proc->p_lock); ainfo = crgetauinfo(cr); if (ainfo == NULL) { crfree(cr); return (EINVAL); } STRUCT_FSET(apinfo, ap_auid, ainfo->ai_auid); STRUCT_FSET(apinfo, ap_asid, ainfo->ai_asid); #ifdef _LP64 if (model == DATAMODEL_ILP32) { dev32_t dev; /* convert internal 64 bit form to 32 bit version */ if (cmpldev(&dev, ainfo->ai_termid.at_port) == 0) { crfree(cr); return (EOVERFLOW); } STRUCT_FSET(apinfo, ap_termid.at_port, dev); } else STRUCT_FSET(apinfo, ap_termid.at_port, ainfo->ai_termid.at_port); #else STRUCT_FSET(apinfo, ap_termid.at_port, ainfo->ai_termid.at_port); #endif STRUCT_FSET(apinfo, ap_termid.at_type, ainfo->ai_termid.at_type); STRUCT_FSET(apinfo, ap_termid.at_addr[0], ainfo->ai_termid.at_addr[0]); STRUCT_FSET(apinfo, ap_termid.at_addr[1], ainfo->ai_termid.at_addr[1]); STRUCT_FSET(apinfo, ap_termid.at_addr[2], ainfo->ai_termid.at_addr[2]); STRUCT_FSET(apinfo, ap_termid.at_addr[3], ainfo->ai_termid.at_addr[3]); STRUCT_FSET(apinfo, ap_mask, ainfo->ai_mask); crfree(cr); if (copyout(STRUCT_BUF(apinfo), data, STRUCT_SIZE(apinfo))) return (EFAULT); return (0); }
/* ARGSUSED */ int setpgrp(int flag, int pid, int pgid) { register proc_t *p = ttoproc(curthread); register int retval = 0; switch (flag) { case 1: /* setpgrp() */ mutex_enter(&pidlock); if (p->p_sessp->s_sidp != p->p_pidp && !pgmembers(p->p_pid)) { mutex_exit(&pidlock); sess_create(); } else mutex_exit(&pidlock); return (p->p_sessp->s_sid); case 3: /* setsid() */ mutex_enter(&pidlock); if (p->p_pgidp == p->p_pidp || pgmembers(p->p_pid)) { mutex_exit(&pidlock); return (set_errno(EPERM)); } mutex_exit(&pidlock); sess_create(); return (p->p_sessp->s_sid); case 5: /* setpgid() */ { mutex_enter(&pidlock); if (pid == 0) pid = p->p_pid; else if (pid < 0 || pid >= maxpid) { mutex_exit(&pidlock); return (set_errno(EINVAL)); } else if (pid != p->p_pid) { for (p = p->p_child; /* empty */; p = p->p_sibling) { if (p == NULL) { mutex_exit(&pidlock); return (set_errno(ESRCH)); } if (p->p_pid == pid) break; } if (p->p_flag & SEXECED) { mutex_exit(&pidlock); return (set_errno(EACCES)); } if (p->p_sessp != ttoproc(curthread)->p_sessp) { mutex_exit(&pidlock); return (set_errno(EPERM)); } } if (p->p_sessp->s_sid == pid) { mutex_exit(&pidlock); return (set_errno(EPERM)); } if (pgid == 0) pgid = p->p_pid; else if (pgid < 0 || pgid >= maxpid) { mutex_exit(&pidlock); return (set_errno(EINVAL)); } if (p->p_pgrp == pgid) { mutex_exit(&pidlock); break; } else if (p->p_pid == pgid) { /* * We need to protect p_pgidp with p_lock because * /proc looks at it while holding only p_lock. */ mutex_enter(&p->p_lock); pgexit(p); pgjoin(p, p->p_pidp); mutex_exit(&p->p_lock); } else { register proc_t *q; if ((q = pgfind(pgid)) == NULL || q->p_sessp != p->p_sessp) { mutex_exit(&pidlock); return (set_errno(EPERM)); } /* * See comment above about p_lock and /proc */ mutex_enter(&p->p_lock); pgexit(p); pgjoin(p, q->p_pgidp); mutex_exit(&p->p_lock); } mutex_exit(&pidlock); break; } case 0: /* getpgrp() */ mutex_enter(&pidlock); retval = p->p_pgrp; mutex_exit(&pidlock); break; case 2: /* getsid() */ case 4: /* getpgid() */ if (pid < 0 || pid >= maxpid) { return (set_errno(EINVAL)); } mutex_enter(&pidlock); if (pid != 0 && p->p_pid != pid && ((p = prfind(pid)) == NULL || p->p_stat == SIDL)) { mutex_exit(&pidlock); return (set_errno(ESRCH)); } if (flag == 2) retval = p->p_sessp->s_sid; else retval = p->p_pgrp; mutex_exit(&pidlock); break; } return (retval); }
/* * Process debugging system call. */ int sys_ptrace(struct proc *p, void *v, register_t *retval) { struct sys_ptrace_args /* { syscallarg(int) req; syscallarg(pid_t) pid; syscallarg(caddr_t) addr; syscallarg(int) data; } */ *uap = v; struct proc *t; /* target thread */ struct process *tr; /* target process */ struct uio uio; struct iovec iov; struct ptrace_io_desc piod; struct ptrace_event pe; struct ptrace_thread_state pts; struct reg *regs; #if defined (PT_SETFPREGS) || defined (PT_GETFPREGS) struct fpreg *fpregs; #endif #if defined (PT_SETXMMREGS) || defined (PT_GETXMMREGS) struct xmmregs *xmmregs; #endif #ifdef PT_WCOOKIE register_t wcookie; #endif int error, write; int temp; int req = SCARG(uap, req); int s; /* "A foolish consistency..." XXX */ switch (req) { case PT_TRACE_ME: t = p; break; /* calls that only operate on the PID */ case PT_READ_I: case PT_READ_D: case PT_WRITE_I: case PT_WRITE_D: case PT_KILL: case PT_ATTACH: case PT_IO: case PT_SET_EVENT_MASK: case PT_GET_EVENT_MASK: case PT_GET_PROCESS_STATE: case PT_GET_THREAD_FIRST: case PT_GET_THREAD_NEXT: default: /* Find the process we're supposed to be operating on. */ if ((t = pfind(SCARG(uap, pid))) == NULL) return (ESRCH); if (t->p_flag & P_THREAD) return (ESRCH); break; /* calls that accept a PID or a thread ID */ case PT_CONTINUE: case PT_DETACH: #ifdef PT_STEP case PT_STEP: #endif case PT_GETREGS: case PT_SETREGS: #ifdef PT_GETFPREGS case PT_GETFPREGS: #endif #ifdef PT_SETFPREGS case PT_SETFPREGS: #endif #ifdef PT_GETXMMREGS case PT_GETXMMREGS: #endif #ifdef PT_SETXMMREGS case PT_SETXMMREGS: #endif if (SCARG(uap, pid) > THREAD_PID_OFFSET) { t = pfind(SCARG(uap, pid) - THREAD_PID_OFFSET); if (t == NULL) return (ESRCH); } else { if ((t = pfind(SCARG(uap, pid))) == NULL) return (ESRCH); if (t->p_flag & P_THREAD) return (ESRCH); } break; } tr = t->p_p; if ((tr->ps_flags & PS_INEXEC) != 0) return (EAGAIN); /* Make sure we can operate on it. */ switch (req) { case PT_TRACE_ME: /* Saying that you're being traced is always legal. */ break; case PT_ATTACH: /* * You can't attach to a process if: * (1) it's the process that's doing the attaching, */ if (tr == p->p_p) return (EINVAL); /* * (2) it's a system process */ if (ISSET(tr->ps_flags, PS_SYSTEM)) return (EPERM); /* * (3) it's already being traced, or */ if (ISSET(tr->ps_flags, PS_TRACED)) return (EBUSY); /* * (4) it's not owned by you, or the last exec * gave us setuid/setgid privs (unless * you're root), or... * * [Note: once PS_SUGID or PS_SUGIDEXEC gets set in * execve(), they stay set until the process does * another execve(). Hence this prevents a setuid * process which revokes its special privileges using * setuid() from being traced. This is good security.] */ if ((tr->ps_ucred->cr_ruid != p->p_ucred->cr_ruid || ISSET(tr->ps_flags, PS_SUGIDEXEC | PS_SUGID)) && (error = suser(p, 0)) != 0) return (error); /* * (4.5) it's not a child of the tracing process. */ if (global_ptrace == 0 && !inferior(tr, p->p_p) && (error = suser(p, 0)) != 0) return (error); /* * (5) ...it's init, which controls the security level * of the entire system, and the system was not * compiled with permanently insecure mode turned * on. */ if ((tr->ps_pid == 1) && (securelevel > -1)) return (EPERM); /* * (6) it's an ancestor of the current process and * not init (because that would create a loop in * the process graph). */ if (tr->ps_pid != 1 && inferior(p->p_p, tr)) return (EINVAL); break; case PT_READ_I: case PT_READ_D: case PT_WRITE_I: case PT_WRITE_D: case PT_IO: case PT_CONTINUE: case PT_KILL: case PT_DETACH: #ifdef PT_STEP case PT_STEP: #endif case PT_SET_EVENT_MASK: case PT_GET_EVENT_MASK: case PT_GET_PROCESS_STATE: case PT_GETREGS: case PT_SETREGS: #ifdef PT_GETFPREGS case PT_GETFPREGS: #endif #ifdef PT_SETFPREGS case PT_SETFPREGS: #endif #ifdef PT_GETXMMREGS case PT_GETXMMREGS: #endif #ifdef PT_SETXMMREGS case PT_SETXMMREGS: #endif #ifdef PT_WCOOKIE case PT_WCOOKIE: #endif /* * You can't do what you want to the process if: * (1) It's not being traced at all, */ if (!ISSET(tr->ps_flags, PS_TRACED)) return (EPERM); /* * (2) it's not being traced by _you_, or */ if (tr->ps_pptr != p->p_p) return (EBUSY); /* * (3) it's not currently stopped. */ if (t->p_stat != SSTOP || !ISSET(tr->ps_flags, PS_WAITED)) return (EBUSY); break; case PT_GET_THREAD_FIRST: case PT_GET_THREAD_NEXT: /* * You can't do what you want to the process if: * (1) It's not being traced at all, */ if (!ISSET(tr->ps_flags, PS_TRACED)) return (EPERM); /* * (2) it's not being traced by _you_, or */ if (tr->ps_pptr != p->p_p) return (EBUSY); /* * Do the work here because the request isn't actually * associated with 't' */ if (SCARG(uap, data) != sizeof(pts)) return (EINVAL); if (req == PT_GET_THREAD_NEXT) { error = copyin(SCARG(uap, addr), &pts, sizeof(pts)); if (error) return (error); t = pfind(pts.pts_tid - THREAD_PID_OFFSET); if (t == NULL || ISSET(t->p_flag, P_WEXIT)) return (ESRCH); if (t->p_p != tr) return (EINVAL); t = TAILQ_NEXT(t, p_thr_link); } else { t = TAILQ_FIRST(&tr->ps_threads); } if (t == NULL) pts.pts_tid = -1; else pts.pts_tid = t->p_pid + THREAD_PID_OFFSET; return (copyout(&pts, SCARG(uap, addr), sizeof(pts))); default: /* It was not a legal request. */ return (EINVAL); } /* Do single-step fixup if needed. */ FIX_SSTEP(t); /* Now do the operation. */ write = 0; *retval = 0; switch (req) { case PT_TRACE_ME: /* Just set the trace flag. */ atomic_setbits_int(&tr->ps_flags, PS_TRACED); tr->ps_oppid = tr->ps_pptr->ps_pid; if (tr->ps_ptstat == NULL) tr->ps_ptstat = malloc(sizeof(*tr->ps_ptstat), M_SUBPROC, M_WAITOK); memset(tr->ps_ptstat, 0, sizeof(*tr->ps_ptstat)); return (0); case PT_WRITE_I: /* XXX no separate I and D spaces */ case PT_WRITE_D: write = 1; temp = SCARG(uap, data); case PT_READ_I: /* XXX no separate I and D spaces */ case PT_READ_D: /* write = 0 done above. */ iov.iov_base = (caddr_t)&temp; iov.iov_len = sizeof(int); uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = (off_t)(vaddr_t)SCARG(uap, addr); uio.uio_resid = sizeof(int); uio.uio_segflg = UIO_SYSSPACE; uio.uio_rw = write ? UIO_WRITE : UIO_READ; uio.uio_procp = p; error = process_domem(p, t, &uio, write ? PT_WRITE_I : PT_READ_I); if (write == 0) *retval = temp; return (error); case PT_IO: error = copyin(SCARG(uap, addr), &piod, sizeof(piod)); if (error) return (error); iov.iov_base = piod.piod_addr; iov.iov_len = piod.piod_len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_offset = (off_t)(vaddr_t)piod.piod_offs; uio.uio_resid = piod.piod_len; uio.uio_segflg = UIO_USERSPACE; uio.uio_procp = p; switch (piod.piod_op) { case PIOD_READ_I: req = PT_READ_I; uio.uio_rw = UIO_READ; break; case PIOD_READ_D: req = PT_READ_D; uio.uio_rw = UIO_READ; break; case PIOD_WRITE_I: req = PT_WRITE_I; uio.uio_rw = UIO_WRITE; break; case PIOD_WRITE_D: req = PT_WRITE_D; uio.uio_rw = UIO_WRITE; break; case PIOD_READ_AUXV: req = PT_READ_D; uio.uio_rw = UIO_READ; temp = tr->ps_emul->e_arglen * sizeof(char *); if (uio.uio_offset > temp) return (EIO); if (uio.uio_resid > temp - uio.uio_offset) uio.uio_resid = temp - uio.uio_offset; piod.piod_len = iov.iov_len = uio.uio_resid; error = process_auxv_offset(p, t, &uio); if (error) return (error); break; default: return (EINVAL); } error = process_domem(p, t, &uio, req); piod.piod_len -= uio.uio_resid; (void) copyout(&piod, SCARG(uap, addr), sizeof(piod)); return (error); #ifdef PT_STEP case PT_STEP: /* * From the 4.4BSD PRM: * "Execution continues as in request PT_CONTINUE; however * as soon as possible after execution of at least one * instruction, execution stops again. [ ... ]" */ #endif case PT_CONTINUE: /* * From the 4.4BSD PRM: * "The data argument is taken as a signal number and the * child's execution continues at location addr as if it * incurred that signal. Normally the signal number will * be either 0 to indicate that the signal that caused the * stop should be ignored, or that value fetched out of * the process's image indicating which signal caused * the stop. If addr is (int *)1 then execution continues * from where it stopped." */ if (SCARG(uap, pid) < THREAD_PID_OFFSET && tr->ps_single) t = tr->ps_single; /* Check that the data is a valid signal number or zero. */ if (SCARG(uap, data) < 0 || SCARG(uap, data) >= NSIG) return (EINVAL); /* If the address parameter is not (int *)1, set the pc. */ if ((int *)SCARG(uap, addr) != (int *)1) if ((error = process_set_pc(t, SCARG(uap, addr))) != 0) goto relebad; #ifdef PT_STEP /* * Arrange for a single-step, if that's requested and possible. */ error = process_sstep(t, req == PT_STEP); if (error) goto relebad; #endif goto sendsig; case PT_DETACH: /* * From the 4.4BSD PRM: * "The data argument is taken as a signal number and the * child's execution continues at location addr as if it * incurred that signal. Normally the signal number will * be either 0 to indicate that the signal that caused the * stop should be ignored, or that value fetched out of * the process's image indicating which signal caused * the stop. If addr is (int *)1 then execution continues * from where it stopped." */ if (SCARG(uap, pid) < THREAD_PID_OFFSET && tr->ps_single) t = tr->ps_single; /* Check that the data is a valid signal number or zero. */ if (SCARG(uap, data) < 0 || SCARG(uap, data) >= NSIG) return (EINVAL); #ifdef PT_STEP /* * Arrange for a single-step, if that's requested and possible. */ error = process_sstep(t, req == PT_STEP); if (error) goto relebad; #endif /* give process back to original parent or init */ if (tr->ps_oppid != tr->ps_pptr->ps_pid) { struct process *ppr; ppr = prfind(tr->ps_oppid); proc_reparent(tr, ppr ? ppr : initprocess); } /* not being traced any more */ tr->ps_oppid = 0; atomic_clearbits_int(&tr->ps_flags, PS_TRACED|PS_WAITED); sendsig: memset(tr->ps_ptstat, 0, sizeof(*tr->ps_ptstat)); /* Finally, deliver the requested signal (or none). */ if (t->p_stat == SSTOP) { t->p_xstat = SCARG(uap, data); SCHED_LOCK(s); setrunnable(t); SCHED_UNLOCK(s); } else { if (SCARG(uap, data) != 0) psignal(t, SCARG(uap, data)); } return (0); relebad: return (error); case PT_KILL: if (SCARG(uap, pid) < THREAD_PID_OFFSET && tr->ps_single) t = tr->ps_single; /* just send the process a KILL signal. */ SCARG(uap, data) = SIGKILL; goto sendsig; /* in PT_CONTINUE, above. */ case PT_ATTACH: /* * As was done in procfs: * Go ahead and set the trace flag. * Save the old parent (it's reset in * _DETACH, and also in kern_exit.c:wait4() * Reparent the process so that the tracing * proc gets to see all the action. * Stop the target. */ atomic_setbits_int(&tr->ps_flags, PS_TRACED); tr->ps_oppid = tr->ps_pptr->ps_pid; if (tr->ps_pptr != p->p_p) proc_reparent(tr, p->p_p); if (tr->ps_ptstat == NULL) tr->ps_ptstat = malloc(sizeof(*tr->ps_ptstat), M_SUBPROC, M_WAITOK); SCARG(uap, data) = SIGSTOP; goto sendsig; case PT_GET_EVENT_MASK: if (SCARG(uap, data) != sizeof(pe)) return (EINVAL); memset(&pe, 0, sizeof(pe)); pe.pe_set_event = tr->ps_ptmask; return (copyout(&pe, SCARG(uap, addr), sizeof(pe))); case PT_SET_EVENT_MASK: if (SCARG(uap, data) != sizeof(pe)) return (EINVAL); if ((error = copyin(SCARG(uap, addr), &pe, sizeof(pe)))) return (error); tr->ps_ptmask = pe.pe_set_event; return (0); case PT_GET_PROCESS_STATE: if (SCARG(uap, data) != sizeof(*tr->ps_ptstat)) return (EINVAL); if (tr->ps_single) tr->ps_ptstat->pe_tid = tr->ps_single->p_pid + THREAD_PID_OFFSET; return (copyout(tr->ps_ptstat, SCARG(uap, addr), sizeof(*tr->ps_ptstat))); case PT_SETREGS: KASSERT((p->p_flag & P_SYSTEM) == 0); if ((error = process_checkioperm(p, tr)) != 0) return (error); regs = malloc(sizeof(*regs), M_TEMP, M_WAITOK); error = copyin(SCARG(uap, addr), regs, sizeof(*regs)); if (error == 0) { error = process_write_regs(t, regs); } free(regs, M_TEMP, sizeof(*regs)); return (error); case PT_GETREGS: KASSERT((p->p_flag & P_SYSTEM) == 0); if ((error = process_checkioperm(p, tr)) != 0) return (error); regs = malloc(sizeof(*regs), M_TEMP, M_WAITOK); error = process_read_regs(t, regs); if (error == 0) error = copyout(regs, SCARG(uap, addr), sizeof (*regs)); free(regs, M_TEMP, sizeof(*regs)); return (error); #ifdef PT_SETFPREGS case PT_SETFPREGS: KASSERT((p->p_flag & P_SYSTEM) == 0); if ((error = process_checkioperm(p, tr)) != 0) return (error); fpregs = malloc(sizeof(*fpregs), M_TEMP, M_WAITOK); error = copyin(SCARG(uap, addr), fpregs, sizeof(*fpregs)); if (error == 0) { error = process_write_fpregs(t, fpregs); } free(fpregs, M_TEMP, sizeof(*fpregs)); return (error); #endif #ifdef PT_GETFPREGS case PT_GETFPREGS: KASSERT((p->p_flag & P_SYSTEM) == 0); if ((error = process_checkioperm(p, tr)) != 0) return (error); fpregs = malloc(sizeof(*fpregs), M_TEMP, M_WAITOK); error = process_read_fpregs(t, fpregs); if (error == 0) error = copyout(fpregs, SCARG(uap, addr), sizeof(*fpregs)); free(fpregs, M_TEMP, sizeof(*fpregs)); return (error); #endif #ifdef PT_SETXMMREGS case PT_SETXMMREGS: KASSERT((p->p_flag & P_SYSTEM) == 0); if ((error = process_checkioperm(p, tr)) != 0) return (error); xmmregs = malloc(sizeof(*xmmregs), M_TEMP, M_WAITOK); error = copyin(SCARG(uap, addr), xmmregs, sizeof(*xmmregs)); if (error == 0) { error = process_write_xmmregs(t, xmmregs); } free(xmmregs, M_TEMP, sizeof(*xmmregs)); return (error); #endif #ifdef PT_GETXMMREGS case PT_GETXMMREGS: KASSERT((p->p_flag & P_SYSTEM) == 0); if ((error = process_checkioperm(p, tr)) != 0) return (error); xmmregs = malloc(sizeof(*xmmregs), M_TEMP, M_WAITOK); error = process_read_xmmregs(t, xmmregs); if (error == 0) error = copyout(xmmregs, SCARG(uap, addr), sizeof(*xmmregs)); free(xmmregs, M_TEMP, sizeof(*xmmregs)); return (error); #endif #ifdef PT_WCOOKIE case PT_WCOOKIE: wcookie = process_get_wcookie (t); return (copyout(&wcookie, SCARG(uap, addr), sizeof (register_t))); #endif } #ifdef DIAGNOSTIC panic("ptrace: impossible"); #endif return 0; }
int signotify(int cmd, siginfo_t *siginfo, signotify_id_t *sn_id) { k_siginfo_t info; signotify_id_t id; proc_t *p; proc_t *cp = curproc; signotifyq_t *snqp; struct cred *cr; sigqueue_t *sqp; sigqhdr_t *sqh; u_longlong_t sid; model_t datamodel = get_udatamodel(); if (copyin(sn_id, &id, sizeof (signotify_id_t))) return (set_errno(EFAULT)); if (id.sn_index >= _SIGNOTIFY_MAX || id.sn_index < 0) return (set_errno(EINVAL)); switch (cmd) { case SN_PROC: /* get snid for the given user address of signotifyid_t */ sid = get_sigid(cp, (caddr_t)sn_id); if (id.sn_pid > 0) { mutex_enter(&pidlock); if ((p = prfind(id.sn_pid)) != NULL) { mutex_enter(&p->p_lock); if (p->p_signhdr != NULL) { snqp = SIGN_PTR(p, id.sn_index); if (snqp->sn_snid == sid) { mutex_exit(&p->p_lock); mutex_exit(&pidlock); return (set_errno(EBUSY)); } } mutex_exit(&p->p_lock); } mutex_exit(&pidlock); } if (copyin_siginfo(datamodel, siginfo, &info)) return (set_errno(EFAULT)); /* The si_code value must indicate the signal will be queued */ if (!sigwillqueue(info.si_signo, info.si_code)) return (set_errno(EINVAL)); if (cp->p_signhdr == NULL) { /* Allocate signotify pool first time */ sqh = sigqhdralloc(sizeof (signotifyq_t), _SIGNOTIFY_MAX); mutex_enter(&cp->p_lock); if (cp->p_signhdr == NULL) { /* hang the pool head on proc */ cp->p_signhdr = sqh; } else { /* another lwp allocated the pool, free ours */ sigqhdrfree(sqh); } } else { mutex_enter(&cp->p_lock); } sqp = sigqalloc(cp->p_signhdr); if (sqp == NULL) { mutex_exit(&cp->p_lock); return (set_errno(EAGAIN)); } cr = CRED(); sqp->sq_info = info; sqp->sq_info.si_pid = cp->p_pid; sqp->sq_info.si_ctid = PRCTID(cp); sqp->sq_info.si_zoneid = getzoneid(); sqp->sq_info.si_uid = crgetruid(cr); /* fill the signotifyq_t fields */ ((signotifyq_t *)sqp)->sn_snid = sid; mutex_exit(&cp->p_lock); /* complete the signotify_id_t fields */ id.sn_index = (signotifyq_t *)sqp - SIGN_PTR(cp, 0); id.sn_pid = cp->p_pid; break; case SN_CANCEL: case SN_SEND: sid = get_sigid(cp, (caddr_t)sn_id); mutex_enter(&pidlock); if ((id.sn_pid <= 0) || ((p = prfind(id.sn_pid)) == NULL)) { mutex_exit(&pidlock); return (set_errno(EINVAL)); } mutex_enter(&p->p_lock); mutex_exit(&pidlock); if (p->p_signhdr == NULL) { mutex_exit(&p->p_lock); return (set_errno(EINVAL)); } snqp = SIGN_PTR(p, id.sn_index); if (snqp->sn_snid == 0) { mutex_exit(&p->p_lock); return (set_errno(EINVAL)); } if (snqp->sn_snid != sid) { mutex_exit(&p->p_lock); return (set_errno(EINVAL)); } snqp->sn_snid = 0; /* cmd == SN_CANCEL or signo == 0 (SIGEV_NONE) */ if (((sigqueue_t *)snqp)->sq_info.si_signo <= 0) cmd = SN_CANCEL; sigqsend(cmd, p, 0, (sigqueue_t *)snqp); mutex_exit(&p->p_lock); id.sn_pid = 0; id.sn_index = 0; break; default : return (set_errno(EINVAL)); } if (copyout(&id, sn_id, sizeof (signotify_id_t))) return (set_errno(EFAULT)); return (0); }
/* * static long rctlsys_set(char *name, rctl_opaque_t *old_rblk, * rctl_opaque_t *new_rblk, int flags) * * Overview * rctlsys_set() is the implementation of the core login of setrctl(2), which * allows the establishment of resource control values. Flags may take on any * of three exclusive values: RCTL_INSERT, RCTL_DELETE, and RCTL_REPLACE. * RCTL_INSERT ignores old_rblk and inserts the value in the appropriate * position in the ordered sequence of resource control values. RCTL_DELETE * ignores old_rblk and deletes the first resource control value matching * (value, priority) in the given resource block. If no matching value is * found, -1 is returned and errno is set to ENOENT. Finally, in the case of * RCTL_REPLACE, old_rblk is used to match (value, priority); the matching * resource control value in the sequence is replaced with the contents of * new_rblk. Again, if no match is found, -1 is returned and errno is set to * ENOENT. * * rctlsys_set() causes a cursor test, which can reactivate resource controls * that have previously fired. */ static long rctlsys_set(char *name, rctl_opaque_t *old_rblk, rctl_opaque_t *new_rblk, int flags) { rctl_val_t *nval; rctl_dict_entry_t *rde; rctl_opaque_t *nblk; rctl_hndl_t hndl; char *kname; size_t klen; long ret = 0; proc_t *pp = NULL; pid_t pid; int action = flags & (~RCTLSYS_ACTION_MASK); rctl_val_t *oval; rctl_val_t *rval1; rctl_val_t *rval2; rctl_val_t *tval; rctl_opaque_t *oblk; if (flags & (~RCTLSYS_MASK)) return (set_errno(EINVAL)); if (action != RCTL_INSERT && action != RCTL_DELETE && action != RCTL_REPLACE) return (set_errno(EINVAL)); if (new_rblk == NULL || name == NULL) return (set_errno(EFAULT)); kname = kmem_alloc(MAXPATHLEN, KM_SLEEP); if (copyinstr(name, kname, MAXPATHLEN, &klen) != 0) { kmem_free(kname, MAXPATHLEN); return (set_errno(EFAULT)); } if ((hndl = rctl_hndl_lookup(kname)) == -1) { kmem_free(kname, MAXPATHLEN); return (set_errno(EINVAL)); } kmem_free(kname, MAXPATHLEN); rde = rctl_dict_lookup_hndl(hndl); nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP); if (copyin(new_rblk, nblk, sizeof (rctl_opaque_t)) == -1) { kmem_free(nblk, sizeof (rctl_opaque_t)); return (set_errno(EFAULT)); } nval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); rctlsys_rblk_xfrm(nblk, NULL, nval, RBX_FROM_BLK | RBX_VAL); if (rctl_invalid_value(rde, nval)) { kmem_free(nblk, sizeof (rctl_opaque_t)); kmem_cache_free(rctl_val_cache, nval); return (set_errno(EINVAL)); } /* allocate what we might need before potentially grabbing p_lock */ oblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP); oval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); rval1 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); rval2 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); if (nval->rcv_privilege == RCPRIV_BASIC) { if (flags & RCTL_USE_RECIPIENT_PID) { pid = nval->rcv_action_recip_pid; /* case for manipulating rctl values on other procs */ if (pid != curproc->p_pid) { /* cannot be other pid on process rctls */ if (rde->rcd_entity == RCENTITY_PROCESS) { ret = set_errno(EINVAL); goto rctlsys_out; } /* * must have privilege to manipulate controls * on other processes */ if (secpolicy_rctlsys(CRED(), B_FALSE) != 0) { ret = set_errno(EACCES); goto rctlsys_out; } pid = nval->rcv_action_recip_pid; mutex_enter(&pidlock); pp = prfind(pid); if (!pp) { mutex_exit(&pidlock); ret = set_errno(ESRCH); goto rctlsys_out; } /* * idle or zombie procs have either not yet * set up their rctls or have already done * their rctl_set_tearoff's. */ if (pp->p_stat == SZOMB || pp->p_stat == SIDL) { mutex_exit(&pidlock); ret = set_errno(ESRCH); goto rctlsys_out; } /* * hold this pp's p_lock to ensure that * it does not do it's rctl_set_tearoff * If we did not do this, we could * potentially add rctls to the entity * with a recipient that is a process * that has exited. */ mutex_enter(&pp->p_lock); mutex_exit(&pidlock); /* * We know that curproc's task, project, * and zone pointers will not change * because functions that change them * call holdlwps(SHOLDFORK1) first. */ /* * verify that the found pp is in the * current task. If it is, then it * is also within the current project * and zone. */ if (rde->rcd_entity == RCENTITY_TASK && pp->p_task != curproc->p_task) { ret = set_errno(ESRCH); goto rctlsys_out; } ASSERT(pp->p_task->tk_proj == curproc->p_task->tk_proj); ASSERT(pp->p_zone == curproc->p_zone); nval->rcv_action_recipient = pp; nval->rcv_action_recip_pid = pid; } else { /* for manipulating rctl values on this proc */ mutex_enter(&curproc->p_lock); pp = curproc; nval->rcv_action_recipient = curproc; nval->rcv_action_recip_pid = curproc->p_pid; } } else { /* RCTL_USE_RECIPIENT_PID not set, use this proc */ mutex_enter(&curproc->p_lock); pp = curproc; nval->rcv_action_recipient = curproc; nval->rcv_action_recip_pid = curproc->p_pid; } } else { /* privileged controls have no recipient pid */ mutex_enter(&curproc->p_lock); pp = curproc; nval->rcv_action_recipient = NULL; nval->rcv_action_recip_pid = -1; } nval->rcv_firing_time = 0; if (action == RCTL_REPLACE) { if (copyin(old_rblk, oblk, sizeof (rctl_opaque_t)) == -1) { ret = set_errno(EFAULT); goto rctlsys_out; } rctlsys_rblk_xfrm(oblk, NULL, oval, RBX_FROM_BLK | RBX_VAL); if (rctl_invalid_value(rde, oval)) { ret = set_errno(EINVAL); goto rctlsys_out; } if (oval->rcv_privilege == RCPRIV_BASIC) { if (!(flags & RCTL_USE_RECIPIENT_PID)) { oval->rcv_action_recipient = curproc; oval->rcv_action_recip_pid = curproc->p_pid; } } else { oval->rcv_action_recipient = NULL; oval->rcv_action_recip_pid = -1; } /* * Find the real value we're attempting to replace on the * sequence, rather than trusting the one delivered from * userland. */ if (ret = rctl_local_get(hndl, NULL, rval1, pp)) { (void) set_errno(ret); goto rctlsys_out; } do { if (rval1->rcv_privilege == RCPRIV_SYSTEM || rctl_val_cmp(oval, rval1, 0) == 0) break; tval = rval1; rval1 = rval2; rval2 = tval; } while (rctl_local_get(hndl, rval2, rval1, pp) == 0); if (rval1->rcv_privilege == RCPRIV_SYSTEM) { if (rctl_val_cmp(oval, rval1, 1) == 0) ret = set_errno(EPERM); else ret = set_errno(ESRCH); goto rctlsys_out; } bcopy(rval1, oval, sizeof (rctl_val_t)); /* * System controls are immutable. */ if (nval->rcv_privilege == RCPRIV_SYSTEM) { ret = set_errno(EPERM); goto rctlsys_out; } /* * Only privileged processes in the global zone can modify * privileged rctls of type RCENTITY_ZONE; replacing privileged * controls with basic ones are not allowed either. Lowering a * lowerable one might be OK for privileged processes in a * non-global zone, but lowerable rctls probably don't make * sense for zones (hence, not modifiable from within a zone). */ if (rde->rcd_entity == RCENTITY_ZONE && (nval->rcv_privilege == RCPRIV_PRIVILEGED || oval->rcv_privilege == RCPRIV_PRIVILEGED) && secpolicy_rctlsys(CRED(), B_TRUE) != 0) { ret = set_errno(EACCES); goto rctlsys_out; } /* * Must be privileged to replace a privileged control with * a basic one. */ if (oval->rcv_privilege == RCPRIV_PRIVILEGED && nval->rcv_privilege != RCPRIV_PRIVILEGED && secpolicy_rctlsys(CRED(), B_FALSE) != 0) { ret = set_errno(EACCES); goto rctlsys_out; } /* * Must have lowerable global property for non-privileged * to lower the value of a privileged control; otherwise must * have sufficient privileges to modify privileged controls * at all. */ if (oval->rcv_privilege == RCPRIV_PRIVILEGED && nval->rcv_privilege == RCPRIV_PRIVILEGED && ((((rde->rcd_flagaction & RCTL_GLOBAL_LOWERABLE) == 0) || oval->rcv_flagaction != nval->rcv_flagaction || oval->rcv_action_signal != nval->rcv_action_signal || oval->rcv_value < nval->rcv_value)) && secpolicy_rctlsys(CRED(), B_FALSE) != 0) { ret = set_errno(EACCES); goto rctlsys_out; } if (ret = rctl_local_replace(hndl, oval, nval, pp)) { (void) set_errno(ret); goto rctlsys_out; } /* ensure that nval is not freed */ nval = NULL; } else if (action == RCTL_INSERT) { /* * System controls are immutable. */ if (nval->rcv_privilege == RCPRIV_SYSTEM) { ret = set_errno(EPERM); goto rctlsys_out; } /* * Only privileged processes in the global zone may add * privileged zone.* rctls. Only privileged processes * may add other privileged rctls. */ if (nval->rcv_privilege == RCPRIV_PRIVILEGED) { if ((rde->rcd_entity == RCENTITY_ZONE && secpolicy_rctlsys(CRED(), B_TRUE) != 0) || (rde->rcd_entity != RCENTITY_ZONE && secpolicy_rctlsys(CRED(), B_FALSE) != 0)) { ret = set_errno(EACCES); goto rctlsys_out; } } /* * Only one basic control is allowed per rctl. * If a basic control is being inserted, delete * any other basic control. */ if ((nval->rcv_privilege == RCPRIV_BASIC) && (rctl_local_get(hndl, NULL, rval1, pp) == 0)) { do { if (rval1->rcv_privilege == RCPRIV_BASIC && rval1->rcv_action_recipient == curproc) { (void) rctl_local_delete(hndl, rval1, pp); if (rctl_local_get(hndl, NULL, rval1, pp) != 0) break; } tval = rval1; rval1 = rval2; rval2 = tval; } while (rctl_local_get(hndl, rval2, rval1, pp) == 0); } if (ret = rctl_local_insert(hndl, nval, pp)) { (void) set_errno(ret); goto rctlsys_out; } /* ensure that nval is not freed */ nval = NULL; } else { /* * RCTL_DELETE */ if (nval->rcv_privilege == RCPRIV_SYSTEM) { ret = set_errno(EPERM); goto rctlsys_out; } if (nval->rcv_privilege == RCPRIV_PRIVILEGED) { if ((rde->rcd_entity == RCENTITY_ZONE && secpolicy_rctlsys(CRED(), B_TRUE) != 0) || (rde->rcd_entity != RCENTITY_ZONE && secpolicy_rctlsys(CRED(), B_FALSE) != 0)) { ret = set_errno(EACCES); goto rctlsys_out; } } if (ret = rctl_local_delete(hndl, nval, pp)) { (void) set_errno(ret); goto rctlsys_out; } } rctlsys_out: if (pp) mutex_exit(&pp->p_lock); kmem_free(nblk, sizeof (rctl_opaque_t)); kmem_free(oblk, sizeof (rctl_opaque_t)); /* only free nval if we did not rctl_local_insert it */ if (nval) kmem_cache_free(rctl_val_cache, nval); kmem_cache_free(rctl_val_cache, oval); kmem_cache_free(rctl_val_cache, rval1); kmem_cache_free(rctl_val_cache, rval2); return (ret); }
/* * Return value: * 1 - exitlwps() failed, call (or continue) lwp_exit() * 0 - restarting init. Return through system call path */ int proc_exit(int why, int what) { kthread_t *t = curthread; klwp_t *lwp = ttolwp(t); proc_t *p = ttoproc(t); zone_t *z = p->p_zone; timeout_id_t tmp_id; int rv; proc_t *q; task_t *tk; vnode_t *exec_vp, *execdir_vp, *cdir, *rdir; sigqueue_t *sqp; lwpdir_t *lwpdir; uint_t lwpdir_sz; tidhash_t *tidhash; uint_t tidhash_sz; ret_tidhash_t *ret_tidhash; refstr_t *cwd; hrtime_t hrutime, hrstime; int evaporate; brand_t *orig_brand = NULL; void *brand_data = NULL; /* * Stop and discard the process's lwps except for the current one, * unless some other lwp beat us to it. If exitlwps() fails then * return and the calling lwp will call (or continue in) lwp_exit(). */ proc_is_exiting(p); if (exitlwps(0) != 0) return (1); mutex_enter(&p->p_lock); if (p->p_ttime > 0) { /* * Account any remaining ticks charged to this process * on its way out. */ (void) task_cpu_time_incr(p->p_task, p->p_ttime); p->p_ttime = 0; } mutex_exit(&p->p_lock); DTRACE_PROC(lwp__exit); DTRACE_PROC1(exit, int, why); /* * Will perform any brand specific proc exit processing. Since this * is always the last lwp, will also perform lwp_exit and free * brand_data, except in the case that the brand has a b_exit_with_sig * handler. In this case we free the brand_data later within this * function. */ mutex_enter(&p->p_lock); if (PROC_IS_BRANDED(p)) { orig_brand = p->p_brand; if (p->p_brand_data != NULL && orig_brand->b_data_size > 0) { brand_data = p->p_brand_data; } lwp_detach_brand_hdlrs(lwp); brand_clearbrand(p, B_FALSE); } mutex_exit(&p->p_lock); /* * Don't let init exit unless zone_start_init() failed its exec, or * we are shutting down the zone or the machine. * * Since we are single threaded, we don't need to lock the * following accesses to zone_proc_initpid. */ if (p->p_pid == z->zone_proc_initpid) { if (z->zone_boot_err == 0 && zone_status_get(z) < ZONE_IS_SHUTTING_DOWN && zone_status_get(global_zone) < ZONE_IS_SHUTTING_DOWN) { if (z->zone_restart_init == B_TRUE) { if (restart_init(what, why) == 0) return (0); } z->zone_init_status = wstat(why, what); (void) zone_kadmin(A_SHUTDOWN, AD_HALT, NULL, CRED()); } /* * Since we didn't or couldn't restart init, we clear * the zone's init state and proceed with exit * processing. */ z->zone_proc_initpid = -1; } lwp_pcb_exit(); /* * Allocate a sigqueue now, before we grab locks. * It will be given to sigcld(), below. * Special case: If we will be making the process disappear * without a trace because it is either: * * an exiting SSYS process, or * * a posix_spawn() vfork child who requests it, * we don't bother to allocate a useless sigqueue. */ evaporate = (p->p_flag & SSYS) || ((p->p_flag & SVFORK) && why == CLD_EXITED && what == _EVAPORATE); if (!evaporate) sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP); /* * revoke any doors created by the process. */ if (p->p_door_list) door_exit(); /* * Release schedctl data structures. */ if (p->p_pagep) schedctl_proc_cleanup(); /* * make sure all pending kaio has completed. */ if (p->p_aio) aio_cleanup_exit(); /* * discard the lwpchan cache. */ if (p->p_lcp != NULL) lwpchan_destroy_cache(0); /* * Clean up any DTrace helper actions or probes for the process. */ if (p->p_dtrace_helpers != NULL) { ASSERT(dtrace_helpers_cleanup != NULL); (*dtrace_helpers_cleanup)(); } /* untimeout the realtime timers */ if (p->p_itimer != NULL) timer_exit(); if ((tmp_id = p->p_alarmid) != 0) { p->p_alarmid = 0; (void) untimeout(tmp_id); } /* * Remove any fpollinfo_t's for this (last) thread from our file * descriptors so closeall() can ASSERT() that they're all gone. */ pollcleanup(); if (p->p_rprof_cyclic != CYCLIC_NONE) { mutex_enter(&cpu_lock); cyclic_remove(p->p_rprof_cyclic); mutex_exit(&cpu_lock); } mutex_enter(&p->p_lock); /* * Clean up any DTrace probes associated with this process. */ if (p->p_dtrace_probes) { ASSERT(dtrace_fasttrap_exit_ptr != NULL); dtrace_fasttrap_exit_ptr(p); } while ((tmp_id = p->p_itimerid) != 0) { p->p_itimerid = 0; mutex_exit(&p->p_lock); (void) untimeout(tmp_id); mutex_enter(&p->p_lock); } lwp_cleanup(); /* * We are about to exit; prevent our resource associations from * being changed. */ pool_barrier_enter(); /* * Block the process against /proc now that we have really * acquired p->p_lock (to manipulate p_tlist at least). */ prbarrier(p); sigfillset(&p->p_ignore); sigemptyset(&p->p_siginfo); sigemptyset(&p->p_sig); sigemptyset(&p->p_extsig); sigemptyset(&t->t_sig); sigemptyset(&t->t_extsig); sigemptyset(&p->p_sigmask); sigdelq(p, t, 0); lwp->lwp_cursig = 0; lwp->lwp_extsig = 0; p->p_flag &= ~(SKILLED | SEXTKILLED); if (lwp->lwp_curinfo) { siginfofree(lwp->lwp_curinfo); lwp->lwp_curinfo = NULL; } t->t_proc_flag |= TP_LWPEXIT; ASSERT(p->p_lwpcnt == 1 && p->p_zombcnt == 0); prlwpexit(t); /* notify /proc */ lwp_hash_out(p, t->t_tid); prexit(p); p->p_lwpcnt = 0; p->p_tlist = NULL; sigqfree(p); term_mstate(t); p->p_mterm = gethrtime(); exec_vp = p->p_exec; execdir_vp = p->p_execdir; p->p_exec = NULLVP; p->p_execdir = NULLVP; mutex_exit(&p->p_lock); pr_free_watched_pages(p); closeall(P_FINFO(p)); /* Free the controlling tty. (freectty() always assumes curproc.) */ ASSERT(p == curproc); (void) freectty(B_TRUE); #if defined(__sparc) if (p->p_utraps != NULL) utrap_free(p); #endif if (p->p_semacct) /* IPC semaphore exit */ semexit(p); rv = wstat(why, what); acct(rv & 0xff); exacct_commit_proc(p, rv); /* * Release any resources associated with C2 auditing */ if (AU_AUDITING()) { /* * audit exit system call */ audit_exit(why, what); } /* * Free address space. */ relvm(); if (exec_vp) { /* * Close this executable which has been opened when the process * was created by getproc(). */ (void) VOP_CLOSE(exec_vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(exec_vp); } if (execdir_vp) VN_RELE(execdir_vp); /* * Release held contracts. */ contract_exit(p); /* * Depart our encapsulating process contract. */ if ((p->p_flag & SSYS) == 0) { ASSERT(p->p_ct_process); contract_process_exit(p->p_ct_process, p, rv); } /* * Remove pool association, and block if requested by pool_do_bind. */ mutex_enter(&p->p_lock); ASSERT(p->p_pool->pool_ref > 0); atomic_dec_32(&p->p_pool->pool_ref); p->p_pool = pool_default; /* * Now that our address space has been freed and all other threads * in this process have exited, set the PEXITED pool flag. This * tells the pools subsystems to ignore this process if it was * requested to rebind this process to a new pool. */ p->p_poolflag |= PEXITED; pool_barrier_exit(); mutex_exit(&p->p_lock); mutex_enter(&pidlock); /* * Delete this process from the newstate list of its parent. We * will put it in the right place in the sigcld in the end. */ delete_ns(p->p_parent, p); /* * Reassign the orphans to the next of kin. * Don't rearrange init's orphanage. */ if ((q = p->p_orphan) != NULL && p != proc_init) { proc_t *nokp = p->p_nextofkin; for (;;) { q->p_nextofkin = nokp; if (q->p_nextorph == NULL) break; q = q->p_nextorph; } q->p_nextorph = nokp->p_orphan; nokp->p_orphan = p->p_orphan; p->p_orphan = NULL; } /* * Reassign the children to init. * Don't try to assign init's children to init. */ if ((q = p->p_child) != NULL && p != proc_init) { struct proc *np; struct proc *initp = proc_init; pid_t zone_initpid = 1; struct proc *zoneinitp = NULL; boolean_t setzonetop = B_FALSE; if (!INGLOBALZONE(curproc)) { zone_initpid = curproc->p_zone->zone_proc_initpid; ASSERT(MUTEX_HELD(&pidlock)); zoneinitp = prfind(zone_initpid); if (zoneinitp != NULL) { initp = zoneinitp; } else { zone_initpid = 1; setzonetop = B_TRUE; } } pgdetach(p); do { np = q->p_sibling; /* * Delete it from its current parent new state * list and add it to init new state list */ delete_ns(q->p_parent, q); q->p_ppid = zone_initpid; q->p_pidflag &= ~(CLDNOSIGCHLD | CLDWAITPID); if (setzonetop) { mutex_enter(&q->p_lock); q->p_flag |= SZONETOP; mutex_exit(&q->p_lock); } q->p_parent = initp; /* * Since q will be the first child, * it will not have a previous sibling. */ q->p_psibling = NULL; if (initp->p_child) { initp->p_child->p_psibling = q; } q->p_sibling = initp->p_child; initp->p_child = q; if (q->p_proc_flag & P_PR_PTRACE) { mutex_enter(&q->p_lock); sigtoproc(q, NULL, SIGKILL); mutex_exit(&q->p_lock); } /* * sigcld() will add the child to parents * newstate list. */ if (q->p_stat == SZOMB) sigcld(q, NULL); } while ((q = np) != NULL); p->p_child = NULL; ASSERT(p->p_child_ns == NULL); } TRACE_1(TR_FAC_PROC, TR_PROC_EXIT, "proc_exit: %p", p); mutex_enter(&p->p_lock); CL_EXIT(curthread); /* tell the scheduler that curthread is exiting */ /* * Have our task accummulate our resource usage data before they * become contaminated by p_cacct etc., and before we renounce * membership of the task. * * We do this regardless of whether or not task accounting is active. * This is to avoid having nonsense data reported for this task if * task accounting is subsequently enabled. The overhead is minimal; * by this point, this process has accounted for the usage of all its * LWPs. We nonetheless do the work here, and under the protection of * pidlock, so that the movement of the process's usage to the task * happens at the same time as the removal of the process from the * task, from the point of view of exacct_snapshot_task_usage(). */ exacct_update_task_mstate(p); hrutime = mstate_aggr_state(p, LMS_USER); hrstime = mstate_aggr_state(p, LMS_SYSTEM); p->p_utime = (clock_t)NSEC_TO_TICK(hrutime) + p->p_cutime; p->p_stime = (clock_t)NSEC_TO_TICK(hrstime) + p->p_cstime; p->p_acct[LMS_USER] += p->p_cacct[LMS_USER]; p->p_acct[LMS_SYSTEM] += p->p_cacct[LMS_SYSTEM]; p->p_acct[LMS_TRAP] += p->p_cacct[LMS_TRAP]; p->p_acct[LMS_TFAULT] += p->p_cacct[LMS_TFAULT]; p->p_acct[LMS_DFAULT] += p->p_cacct[LMS_DFAULT]; p->p_acct[LMS_KFAULT] += p->p_cacct[LMS_KFAULT]; p->p_acct[LMS_USER_LOCK] += p->p_cacct[LMS_USER_LOCK]; p->p_acct[LMS_SLEEP] += p->p_cacct[LMS_SLEEP]; p->p_acct[LMS_WAIT_CPU] += p->p_cacct[LMS_WAIT_CPU]; p->p_acct[LMS_STOPPED] += p->p_cacct[LMS_STOPPED]; p->p_ru.minflt += p->p_cru.minflt; p->p_ru.majflt += p->p_cru.majflt; p->p_ru.nswap += p->p_cru.nswap; p->p_ru.inblock += p->p_cru.inblock; p->p_ru.oublock += p->p_cru.oublock; p->p_ru.msgsnd += p->p_cru.msgsnd; p->p_ru.msgrcv += p->p_cru.msgrcv; p->p_ru.nsignals += p->p_cru.nsignals; p->p_ru.nvcsw += p->p_cru.nvcsw; p->p_ru.nivcsw += p->p_cru.nivcsw; p->p_ru.sysc += p->p_cru.sysc; p->p_ru.ioch += p->p_cru.ioch; p->p_stat = SZOMB; p->p_proc_flag &= ~P_PR_PTRACE; p->p_wdata = what; p->p_wcode = (char)why; cdir = PTOU(p)->u_cdir; rdir = PTOU(p)->u_rdir; cwd = PTOU(p)->u_cwd; ASSERT(cdir != NULL || p->p_parent == &p0); /* * Release resource controls, as they are no longer enforceable. */ rctl_set_free(p->p_rctls); /* * Decrement tk_nlwps counter for our task.max-lwps resource control. * An extended accounting record, if that facility is active, is * scheduled to be written. We cannot give up task and project * membership at this point because that would allow zombies to escape * from the max-processes resource controls. Zombies stay in their * current task and project until the process table slot is released * in freeproc(). */ tk = p->p_task; mutex_enter(&p->p_zone->zone_nlwps_lock); tk->tk_nlwps--; tk->tk_proj->kpj_nlwps--; p->p_zone->zone_nlwps--; mutex_exit(&p->p_zone->zone_nlwps_lock); /* * Clear the lwp directory and the lwpid hash table * now that /proc can't bother us any more. * We free the memory below, after dropping p->p_lock. */ lwpdir = p->p_lwpdir; lwpdir_sz = p->p_lwpdir_sz; tidhash = p->p_tidhash; tidhash_sz = p->p_tidhash_sz; ret_tidhash = p->p_ret_tidhash; p->p_lwpdir = NULL; p->p_lwpfree = NULL; p->p_lwpdir_sz = 0; p->p_tidhash = NULL; p->p_tidhash_sz = 0; p->p_ret_tidhash = NULL; /* * If the process has context ops installed, call the exit routine * on behalf of this last remaining thread. Normally exitpctx() is * called during thread_exit() or lwp_exit(), but because this is the * last thread in the process, we must call it here. By the time * thread_exit() is called (below), the association with the relevant * process has been lost. * * We also free the context here. */ if (p->p_pctx) { kpreempt_disable(); exitpctx(p); kpreempt_enable(); freepctx(p, 0); } /* * curthread's proc pointer is changed to point to the 'sched' * process for the corresponding zone, except in the case when * the exiting process is in fact a zsched instance, in which * case the proc pointer is set to p0. We do so, so that the * process still points at the right zone when we call the VN_RELE() * below. * * This is because curthread's original proc pointer can be freed as * soon as the child sends a SIGCLD to its parent. We use zsched so * that for user processes, even in the final moments of death, the * process is still associated with its zone. */ if (p != t->t_procp->p_zone->zone_zsched) t->t_procp = t->t_procp->p_zone->zone_zsched; else t->t_procp = &p0; mutex_exit(&p->p_lock); if (!evaporate) { /* * The brand specific code only happens when the brand has a * function to call in place of sigcld, the data itself still * existed, and the parent of the exiting process is not the * global zone init. If the parent is the global zone init, * then the process was reparented, and we don't want brand * code delivering possibly strange signals to init. Also, init * is not branded, so any brand specific exit data will not be * picked up by init anyway. * It is assumed by this code that any brand where * b_exit_with_sig == NULL, will free its own brand_data rather * than letting this piece of code free it. */ if (orig_brand != NULL && orig_brand->b_ops->b_exit_with_sig != NULL && brand_data != NULL && p->p_ppid != 1) { /* * The code for _fini that could unload the brand_t * blocks until the count of zones using the module * reaches zero. Zones decrement the refcount on their * brands only after all user tasks in that zone have * exited and been waited on. The decrement on the * brand's refcount happen in zone_destroy(). That * depends on zone_shutdown() having been completed. * zone_shutdown() includes a call to zone_empty(), * where the zone waits for itself to reach the state * ZONE_IS_EMPTY. This state is only set in either * zone_shutdown(), when there are no user processes as * the zone enters this function, or in * zone_task_rele(). zone_task_rele() is called from * code triggered by waiting on processes, not by the * processes exiting through proc_exit(). This means * all the branded processes that could exist for a * specific brand_t must exit and get reaped before the * refcount on the brand_t can reach 0. _fini will * never unload the corresponding brand module before * proc_exit finishes execution for all processes * branded with a particular brand_t, which makes the * operation below safe to do. Brands that wish to use * this mechanism must wait in _fini as described * above. */ orig_brand->b_ops->b_exit_with_sig(p, sqp, brand_data); } else { p->p_pidflag &= ~CLDPEND; sigcld(p, sqp); } if (brand_data != NULL) { kmem_free(brand_data, orig_brand->b_data_size); brand_data = NULL; orig_brand = NULL; } } else { /* * Do what sigcld() would do if the disposition * of the SIGCHLD signal were set to be ignored. */ cv_broadcast(&p->p_srwchan_cv); freeproc(p); } mutex_exit(&pidlock); /* * We don't release u_cdir and u_rdir until SZOMB is set. * This protects us against dofusers(). */ if (cdir) VN_RELE(cdir); if (rdir) VN_RELE(rdir); if (cwd) refstr_rele(cwd); /* * task_rele() may ultimately cause the zone to go away (or * may cause the last user process in a zone to go away, which * signals zsched to go away). So prior to this call, we must * no longer point at zsched. */ t->t_procp = &p0; kmem_free(lwpdir, lwpdir_sz * sizeof (lwpdir_t)); kmem_free(tidhash, tidhash_sz * sizeof (tidhash_t)); while (ret_tidhash != NULL) { ret_tidhash_t *next = ret_tidhash->rth_next; kmem_free(ret_tidhash->rth_tidhash, ret_tidhash->rth_tidhash_sz * sizeof (tidhash_t)); kmem_free(ret_tidhash, sizeof (*ret_tidhash)); ret_tidhash = next; } thread_exit(); /* NOTREACHED */ }