Print this page
OS-5598 newproc() performs inadequate clean-up after failed lwp_create()
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Approved by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-4818 contract template disappears on exec
OS-4825 cgroup user agent should be launched from the kernel
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
OS-4460 exec brands processes that still have multiple threads
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
OS-4151 setbrand hooks should be sane during fork
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
OS-4129 lxbrand should not abuse p_brand_data for storing exit signal
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Joshua M. Clulow <jmc@joyent.com>

*** 19,29 **** * CDDL HEADER END */ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2013, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ --- 19,29 ---- * CDDL HEADER END */ /* * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2016, Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */
*** 82,91 **** --- 82,92 ---- static int64_t cfork(int, int, int); static int getproc(proc_t **, pid_t, uint_t); #define GETPROC_USER 0x0 #define GETPROC_KERNEL 0x1 + #define GETPROC_ZSCHED 0x2 static void fork_fail(proc_t *); static void forklwp_fail(proc_t *); int fork_fail_pending;
*** 123,139 **** default: return ((int64_t)set_errno(EINVAL)); } } /* 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; --- 124,163 ---- default: return ((int64_t)set_errno(EINVAL)); } } + /* + * Remove the associations of a child process from its parent and siblings. + */ + static void + disown_proc(proc_t *pp, proc_t *cp) + { + proc_t **orphpp; + + ASSERT(MUTEX_HELD(&pidlock)); + + orphpp = &pp->p_orphan; + while (*orphpp != cp) + orphpp = &(*orphpp)->p_nextorph; + *orphpp = cp->p_nextorph; + + if (pp->p_child == cp) + pp->p_child = cp->p_sibling; + if (cp->p_sibling) + cp->p_sibling->p_psibling = cp->p_psibling; + if (cp->p_psibling) + cp->p_psibling->p_sibling = cp->p_sibling; + } + /* ARGSUSED */ static int64_t cfork(int isvfork, int isfork1, int flags) { proc_t *p = ttoproc(curthread); struct as *as; ! proc_t *cp; klwp_t *clone; kthread_t *t; task_t *tk; rval_t r; int error;
*** 264,283 **** if (error != 0) { mutex_enter(&p->p_lock); sprunlock(p); fork_fail(cp); mutex_enter(&pidlock); ! orphpp = &p->p_orphan; ! while (*orphpp != cp) ! orphpp = &(*orphpp)->p_nextorph; ! *orphpp = cp->p_nextorph; ! if (p->p_child == cp) ! p->p_child = cp->p_sibling; ! if (cp->p_sibling) ! cp->p_sibling->p_psibling = cp->p_psibling; ! if (cp->p_psibling) ! cp->p_psibling->p_sibling = cp->p_sibling; mutex_enter(&cp->p_lock); tk = cp->p_task; task_detach(cp); ASSERT(cp->p_pool->pool_ref > 0); atomic_dec_32(&cp->p_pool->pool_ref); --- 288,298 ---- if (error != 0) { mutex_enter(&p->p_lock); sprunlock(p); fork_fail(cp); mutex_enter(&pidlock); ! disown_proc(p, cp); mutex_enter(&cp->p_lock); tk = cp->p_task; task_detach(cp); ASSERT(cp->p_pool->pool_ref > 0); atomic_dec_32(&cp->p_pool->pool_ref);
*** 638,657 **** task_detach(cp); ASSERT(cp->p_pool->pool_ref > 0); atomic_dec_32(&cp->p_pool->pool_ref); mutex_exit(&cp->p_lock); ! orphpp = &p->p_orphan; ! while (*orphpp != cp) ! orphpp = &(*orphpp)->p_nextorph; ! *orphpp = cp->p_nextorph; ! if (p->p_child == cp) ! p->p_child = cp->p_sibling; ! if (cp->p_sibling) ! cp->p_sibling->p_psibling = cp->p_psibling; ! if (cp->p_psibling) ! cp->p_psibling->p_sibling = cp->p_sibling; pid_exit(cp, tk); mutex_exit(&pidlock); task_rele(tk); --- 653,663 ---- task_detach(cp); ASSERT(cp->p_pool->pool_ref > 0); atomic_dec_32(&cp->p_pool->pool_ref); mutex_exit(&cp->p_lock); ! disown_proc(p, cp); pid_exit(cp, tk); mutex_exit(&pidlock); task_rele(tk);
*** 694,704 **** if (cp->p_execdir) VN_RELE(cp->p_execdir); if (PTOU(curproc)->u_cwd) refstr_rele(PTOU(curproc)->u_cwd); if (PROC_IS_BRANDED(cp)) { ! brand_clearbrand(cp, B_TRUE); } } /* * Clean up the lwps already created for this child process. --- 700,710 ---- if (cp->p_execdir) VN_RELE(cp->p_execdir); if (PTOU(curproc)->u_cwd) refstr_rele(PTOU(curproc)->u_cwd); if (PROC_IS_BRANDED(cp)) { ! brand_clearbrand(cp, B_FALSE); } } /* * Clean up the lwps already created for this child process.
*** 743,753 **** if (t->t_door != NULL) { kmem_free(t->t_door, sizeof (door_data_t)); t->t_door = NULL; } ! lwp_ctmpl_clear(ttolwp(t)); /* * Remove the thread from the all threads list. * We need to hold pidlock for this. */ --- 749,759 ---- if (t->t_door != NULL) { kmem_free(t->t_door, sizeof (door_data_t)); t->t_door = NULL; } ! lwp_ctmpl_clear(ttolwp(t), B_FALSE); /* * Remove the thread from the all threads list. * We need to hold pidlock for this. */
*** 780,789 **** --- 786,798 ---- extern struct as kas; /* * fork a kernel process. + * + * Passing a pid argument of -1 indicates that the new process should be + * launched as a child of 'zsched' within the zone. */ int newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct, pid_t pid) {
*** 798,807 **** --- 807,817 ---- if (CLASS_KERNEL(cid)) { rctl_alloc_gp_t *init_gp; rctl_set_t *init_set; ASSERT(pid != 1); + ASSERT(pid >= 0); if (getproc(&p, pid, GETPROC_KERNEL) < 0) return (EAGAIN); /*
*** 841,852 **** } else { rctl_alloc_gp_t *init_gp, *default_gp; rctl_set_t *init_set; task_t *tk, *tk_old; klwp_t *lwp; ! if (getproc(&p, pid, GETPROC_USER) < 0) return (EAGAIN); /* * init creates a new task, distinct from the task * containing kernel "processes". */ --- 851,872 ---- } else { rctl_alloc_gp_t *init_gp, *default_gp; rctl_set_t *init_set; task_t *tk, *tk_old; klwp_t *lwp; + boolean_t pzsched = B_FALSE; + int flag = GETPROC_USER; ! /* Handle a new user-level thread as child of zsched. */ ! if (pid < 0) { ! VERIFY(curzone != global_zone); ! flag = GETPROC_ZSCHED; ! pzsched = B_TRUE; ! pid = 0; ! } ! ! if (getproc(&p, pid, flag) < 0) return (EAGAIN); /* * init creates a new task, distinct from the task * containing kernel "processes". */
*** 884,910 **** rctl_prealloc_destroy(init_gp); if ((lwp = lwp_create(pc, arg, 0, p, TS_STOPPED, pri, &curthread->t_hold, cid, 1)) == NULL) { task_t *tk; fork_fail(p); mutex_enter(&pidlock); mutex_enter(&p->p_lock); tk = p->p_task; task_detach(p); ASSERT(p->p_pool->pool_ref > 0); atomic_add_32(&p->p_pool->pool_ref, -1); mutex_exit(&p->p_lock); pid_exit(p, tk); mutex_exit(&pidlock); task_rele(tk); - return (EAGAIN); } t = lwptot(lwp); ! ctp = contract_process_fork(sys_process_tmpl, p, curproc, B_FALSE); ASSERT(ctp != NULL); if (ct != NULL) *ct = &ctp->conp_contract; } --- 904,934 ---- rctl_prealloc_destroy(init_gp); if ((lwp = lwp_create(pc, arg, 0, p, TS_STOPPED, pri, &curthread->t_hold, cid, 1)) == NULL) { task_t *tk; + fork_fail(p); mutex_enter(&pidlock); + disown_proc(p->p_parent, p); + mutex_enter(&p->p_lock); tk = p->p_task; task_detach(p); ASSERT(p->p_pool->pool_ref > 0); atomic_add_32(&p->p_pool->pool_ref, -1); mutex_exit(&p->p_lock); + pid_exit(p, tk); mutex_exit(&pidlock); task_rele(tk); return (EAGAIN); } t = lwptot(lwp); ! ctp = contract_process_fork(sys_process_tmpl, p, ! (pzsched ? curproc->p_zone->zone_zsched : curproc), B_FALSE); ASSERT(ctp != NULL); if (ct != NULL) *ct = &ctp->conp_contract; }
*** 941,951 **** --- 965,979 ---- int rctlfail = 0; if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) return (-1); /* no point in starting new processes */ + if (flags & GETPROC_ZSCHED) { + pp = curproc->p_zone->zone_zsched; + } else { pp = (flags & GETPROC_KERNEL) ? &p0 : curproc; + } task = pp->p_task; proj = task->tk_proj; zone = pp->p_zone; mutex_enter(&pp->p_lock);
*** 1002,1011 **** --- 1030,1042 ---- */ cp->p_zone = pp->p_zone; cp->p_t1_lgrpid = LGRP_NONE; cp->p_tr_lgrpid = LGRP_NONE; + /* Default to native brand initially */ + cp->p_brand = &native_brand; + if ((newpid = pid_allocate(cp, pid, PID_ALLOC_PROC)) == -1) { if (nproc == v.v_proc) { CPU_STATS_ADDQ(CPU, sys, procovf, 1); cmn_err(CE_WARN, "out of processes"); }
*** 1069,1081 **** cp->p_ignore = pp->p_ignore; cp->p_siginfo = pp->p_siginfo; cp->p_flag = pp->p_flag & (SJCTL|SNOWAIT|SNOCD); cp->p_sessp = pp->p_sessp; sess_hold(pp); - cp->p_brand = pp->p_brand; - if (PROC_IS_BRANDED(pp)) - BROP(pp)->b_copy_procdata(cp, pp); cp->p_bssbase = pp->p_bssbase; cp->p_brkbase = pp->p_brkbase; cp->p_brksize = pp->p_brksize; cp->p_brkpageszc = pp->p_brkpageszc; cp->p_stksize = pp->p_stksize; --- 1100,1109 ----
*** 1151,1160 **** --- 1179,1200 ---- else task_attach(pp->p_task, cp); mutex_exit(&cp->p_lock); mutex_exit(&pidlock); + if (PROC_IS_BRANDED(pp)) { + /* + * The only reason why process branding should fail is when + * the procedure is complicated by multiple LWPs on the scene. + * With an LWP count of 0, this newly allocated process has no + * reason to fail branding. + */ + VERIFY0(brand_setbrand(cp, B_FALSE)); + + BROP(pp)->b_copy_procdata(cp, pp); + } + avl_create(&cp->p_ct_held, contract_compar, sizeof (contract_t), offsetof(contract_t, ct_ctlist)); /* * Duplicate any audit information kept in the process table