/* ARGSUSED */ static int64_t cfork(int isvfork, int isfork1, int flags) { proc_t *p = ttoproc(curthread); struct as *as; proc_t *cp, **orphpp; klwp_t *clone; kthread_t *t; task_t *tk; rval_t r; int error; int i; rctl_set_t *dup_set; rctl_alloc_gp_t *dup_gp; rctl_entity_p_t e; lwpdir_t *ldp; lwpent_t *lep; lwpent_t *clep; /* * Allow only these two flags. */ if ((flags & ~(FORK_NOSIGCHLD | FORK_WAITPID)) != 0) { error = EINVAL; goto forkerr; } /* * fork is not supported for the /proc agent lwp. */ if (curthread == p->p_agenttp) { error = ENOTSUP; goto forkerr; } if ((error = secpolicy_basic_fork(CRED())) != 0) goto forkerr; /* * If the calling lwp is doing a fork1() then the * other lwps in this process are not duplicated and * don't need to be held where their kernel stacks can be * cloned. If doing forkall(), the process is held with * SHOLDFORK, so that the lwps are at a point where their * stacks can be copied which is on entry or exit from * the kernel. */ if (!holdlwps(isfork1 ? SHOLDFORK1 : SHOLDFORK)) { aston(curthread); error = EINTR; goto forkerr; } #if defined(__sparc) /* * Ensure that the user stack is fully constructed * before creating the child process structure. */ (void) flush_user_windows_to_stack(NULL); #endif mutex_enter(&p->p_lock); /* * If this is vfork(), cancel any suspend request we might * have gotten from some other thread via lwp_suspend(). * Otherwise we could end up with a deadlock on return * from the vfork() in both the parent and the child. */ if (isvfork) curthread->t_proc_flag &= ~TP_HOLDLWP; /* * Prevent our resource set associations from being changed during fork. */ pool_barrier_enter(); mutex_exit(&p->p_lock); /* * Create a child proc struct. Place a VN_HOLD on appropriate vnodes. */ if (getproc(&cp, 0) < 0) { mutex_enter(&p->p_lock); pool_barrier_exit(); continuelwps(p); mutex_exit(&p->p_lock); error = EAGAIN; goto forkerr; } TRACE_2(TR_FAC_PROC, TR_PROC_FORK, "proc_fork:cp %p p %p", cp, p); /* * Assign an address space to child */ if (isvfork) { /* * Clear any watched areas and remember the * watched pages for restoring in vfwait(). */ as = p->p_as; if (avl_numnodes(&as->a_wpage) != 0) { AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); as_clearwatch(as); p->p_wpage = as->a_wpage; avl_create(&as->a_wpage, wp_compare, sizeof (struct watched_page), offsetof(struct watched_page, wp_link)); AS_LOCK_EXIT(as, &as->a_lock); } cp->p_as = as; cp->p_flag |= SVFORK; } else {
/* * Common code for pr_mappage() and pr_unmappage(). */ static int pr_do_mappage(caddr_t addr, size_t size, int mapin, enum seg_rw rw, int kernel) { proc_t *p = curproc; struct as *as = p->p_as; char *eaddr = addr + size; int prot_rw = rw_to_prot(rw); int xrw = rw_to_index(rw); int rv = 0; struct watched_page *pwp; struct watched_page tpw; avl_index_t where; uint_t prot; ASSERT(as != &kas); startover: ASSERT(rv == 0); if (avl_numnodes(&as->a_wpage) == 0) return (0); /* * as->a_wpage can only be changed while the process is totally stopped. * Don't grab p_lock here. Holding p_lock while grabbing the address * space lock leads to deadlocks with the clock thread. Note that if an * as_fault() is servicing a fault to a watched page on behalf of an * XHAT provider, watchpoint will be temporarily cleared (and wp_prot * will be set to wp_oprot). Since this is done while holding as writer * lock, we need to grab as lock (reader lock is good enough). * * p_maplock prevents simultaneous execution of this function. Under * normal circumstances, holdwatch() will stop all other threads, so the * lock isn't really needed. But there may be multiple threads within * stop() when SWATCHOK is set, so we need to handle multiple threads * at once. See holdwatch() for the details of this dance. */ mutex_enter(&p->p_maplock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); tpw.wp_vaddr = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK); if ((pwp = avl_find(&as->a_wpage, &tpw, &where)) == NULL) pwp = avl_nearest(&as->a_wpage, where, AVL_AFTER); for (; pwp != NULL && pwp->wp_vaddr < eaddr; pwp = AVL_NEXT(&as->a_wpage, pwp)) { /* * If the requested protection has not been * removed, we need not remap this page. */ prot = pwp->wp_prot; if (kernel || (prot & PROT_USER)) if (prot & prot_rw) continue; /* * If the requested access does not exist in the page's * original protections, we need not remap this page. * If the page does not exist yet, we can't test it. */ if ((prot = pwp->wp_oprot) != 0) { if (!(kernel || (prot & PROT_USER))) continue; if (!(prot & prot_rw)) continue; } if (mapin) { /* * Before mapping the page in, ensure that * all other lwps are held in the kernel. */ if (p->p_mapcnt == 0) { /* * Release as lock while in holdwatch() * in case other threads need to grab it. */ AS_LOCK_EXIT(as, &as->a_lock); mutex_exit(&p->p_maplock); if (holdwatch() != 0) { /* * We stopped in holdwatch(). * Start all over again because the * watched page list may have changed. */ goto startover; } mutex_enter(&p->p_maplock); AS_LOCK_ENTER(as, &as->a_lock, RW_READER); } p->p_mapcnt++; } addr = pwp->wp_vaddr; rv++; prot = pwp->wp_prot; if (mapin) { if (kernel) pwp->wp_kmap[xrw]++; else pwp->wp_umap[xrw]++; pwp->wp_flags |= WP_NOWATCH; if (pwp->wp_kmap[X] + pwp->wp_umap[X]) /* cannot have exec-only protection */ prot |= PROT_READ|PROT_EXEC; if (pwp->wp_kmap[R] + pwp->wp_umap[R]) prot |= PROT_READ; if (pwp->wp_kmap[W] + pwp->wp_umap[W]) /* cannot have write-only protection */ prot |= PROT_READ|PROT_WRITE; #if 0 /* damned broken mmu feature! */ if (sum(pwp->wp_umap) == 0) prot &= ~PROT_USER; #endif } else { ASSERT(pwp->wp_flags & WP_NOWATCH); if (kernel) { ASSERT(pwp->wp_kmap[xrw] != 0); --pwp->wp_kmap[xrw]; } else { ASSERT(pwp->wp_umap[xrw] != 0); --pwp->wp_umap[xrw]; } if (sum(pwp->wp_kmap) + sum(pwp->wp_umap) == 0) pwp->wp_flags &= ~WP_NOWATCH; else { if (pwp->wp_kmap[X] + pwp->wp_umap[X]) /* cannot have exec-only protection */ prot |= PROT_READ|PROT_EXEC; if (pwp->wp_kmap[R] + pwp->wp_umap[R]) prot |= PROT_READ; if (pwp->wp_kmap[W] + pwp->wp_umap[W]) /* cannot have write-only protection */ prot |= PROT_READ|PROT_WRITE; #if 0 /* damned broken mmu feature! */ if (sum(pwp->wp_umap) == 0) prot &= ~PROT_USER; #endif } } if (pwp->wp_oprot != 0) { /* if page exists */ struct seg *seg; uint_t oprot; int err, retrycnt = 0; AS_LOCK_EXIT(as, &as->a_lock); AS_LOCK_ENTER(as, &as->a_lock, RW_WRITER); retry: seg = as_segat(as, addr); ASSERT(seg != NULL); SEGOP_GETPROT(seg, addr, 0, &oprot); if (prot != oprot) { err = SEGOP_SETPROT(seg, addr, PAGESIZE, prot); if (err == IE_RETRY) { ASSERT(retrycnt == 0); retrycnt++; goto retry; } } AS_LOCK_EXIT(as, &as->a_lock); } else AS_LOCK_EXIT(as, &as->a_lock); /* * When all pages are mapped back to their normal state, * continue the other lwps. */ if (!mapin) { ASSERT(p->p_mapcnt > 0); p->p_mapcnt--; if (p->p_mapcnt == 0) { mutex_exit(&p->p_maplock); mutex_enter(&p->p_lock); continuelwps(p); mutex_exit(&p->p_lock); mutex_enter(&p->p_maplock); } } AS_LOCK_ENTER(as, &as->a_lock, RW_READER); } AS_LOCK_EXIT(as, &as->a_lock); mutex_exit(&p->p_maplock); return (rv); }
/* * taskid_t tasksys_settaskid(projid_t projid, uint_t flags); * * Overview * Place the calling process in a new task if sufficiently privileged. If the * present task is finalized, the process may not create a new task. * * Return values * 0 on success, errno on failure. */ static long tasksys_settaskid(projid_t projid, uint_t flags) { proc_t *p = ttoproc(curthread); kproject_t *oldpj; kproject_t *kpj; task_t *tk, *oldtk; rctl_entity_p_t e; zone_t *zone; int rctlfail = 0; if (secpolicy_tasksys(CRED()) != 0) return (set_errno(EPERM)); if (projid < 0 || projid > MAXPROJID) return (set_errno(EINVAL)); if (flags & ~TASK_FINAL) return (set_errno(EINVAL)); mutex_enter(&pidlock); if (p->p_task->tk_flags & TASK_FINAL) { mutex_exit(&pidlock); return (set_errno(EACCES)); } mutex_exit(&pidlock); /* * Try to stop all other lwps in the process while we're changing * our project. This way, curthread doesn't need to grab its own * thread_lock to find its project ID (see curprojid()). If this * is the /proc agent lwp, we know that the other lwps are already * held. If we failed to hold all lwps, bail out and return EINTR. */ if (curthread != p->p_agenttp && !holdlwps(SHOLDFORK1)) return (set_errno(EINTR)); /* * Put a hold on our new project and make sure that nobody is * trying to bind it to a pool while we're joining. */ kpj = project_hold_by_id(projid, p->p_zone, PROJECT_HOLD_INSERT); e.rcep_p.proj = kpj; e.rcep_t = RCENTITY_PROJECT; mutex_enter(&p->p_lock); oldpj = p->p_task->tk_proj; zone = p->p_zone; mutex_enter(&zone->zone_nlwps_lock); mutex_enter(&zone->zone_mem_lock); if (kpj->kpj_nlwps + p->p_lwpcnt > kpj->kpj_nlwps_ctl) if (rctl_test_entity(rc_project_nlwps, kpj->kpj_rctls, p, &e, p->p_lwpcnt, 0) & RCT_DENY) rctlfail = 1; if (kpj->kpj_ntasks + 1 > kpj->kpj_ntasks_ctl) if (rctl_test_entity(rc_project_ntasks, kpj->kpj_rctls, p, &e, 1, 0) & RCT_DENY) rctlfail = 1; if (kpj->kpj_data.kpd_locked_mem + p->p_locked_mem > kpj->kpj_data.kpd_locked_mem_ctl) if (rctl_test_entity(rc_project_locked_mem, kpj->kpj_rctls, p, &e, p->p_locked_mem, 0) & RCT_DENY) rctlfail = 1; mutex_enter(&(kpj->kpj_data.kpd_crypto_lock)); if (kpj->kpj_data.kpd_crypto_mem + p->p_crypto_mem > kpj->kpj_data.kpd_crypto_mem_ctl) if (rctl_test_entity(rc_project_crypto_mem, kpj->kpj_rctls, p, &e, p->p_crypto_mem, 0) & RCT_DENY) rctlfail = 1; if (rctlfail) { mutex_exit(&(kpj->kpj_data.kpd_crypto_lock)); mutex_exit(&zone->zone_mem_lock); mutex_exit(&zone->zone_nlwps_lock); if (curthread != p->p_agenttp) continuelwps(p); mutex_exit(&p->p_lock); return (set_errno(EAGAIN)); } kpj->kpj_data.kpd_crypto_mem += p->p_crypto_mem; mutex_exit(&(kpj->kpj_data.kpd_crypto_lock)); kpj->kpj_data.kpd_locked_mem += p->p_locked_mem; kpj->kpj_nlwps += p->p_lwpcnt; kpj->kpj_ntasks++; oldpj->kpj_data.kpd_locked_mem -= p->p_locked_mem; mutex_enter(&(oldpj->kpj_data.kpd_crypto_lock)); oldpj->kpj_data.kpd_crypto_mem -= p->p_crypto_mem; mutex_exit(&(oldpj->kpj_data.kpd_crypto_lock)); oldpj->kpj_nlwps -= p->p_lwpcnt; mutex_exit(&zone->zone_mem_lock); mutex_exit(&zone->zone_nlwps_lock); mutex_exit(&p->p_lock); mutex_enter(&kpj->kpj_poolbind); tk = task_create(projid, curproc->p_zone); mutex_enter(&cpu_lock); /* * Returns with p_lock held. */ oldtk = task_join(tk, flags); if (curthread != p->p_agenttp) continuelwps(p); mutex_exit(&p->p_lock); mutex_exit(&cpu_lock); mutex_exit(&kpj->kpj_poolbind); task_rele(oldtk); project_rele(kpj); return (tk->tk_tkid); }