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,11 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
 /*        All Rights Reserved   */
 

@@ -82,10 +82,11 @@
 
 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,17 +124,40 @@
         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, **orphpp;
+        proc_t *cp;
         klwp_t *clone;
         kthread_t *t;
         task_t *tk;
         rval_t  r;
         int error;

@@ -264,20 +288,11 @@
                 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;
+                        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,20 +653,11 @@
         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;
+        disown_proc(p, cp);
         pid_exit(cp, tk);
         mutex_exit(&pidlock);
 
         task_rele(tk);
 

@@ -694,11 +700,11 @@
         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);
+                brand_clearbrand(cp, B_FALSE);
         }
 }
 
 /*
  * Clean up the lwps already created for this child process.

@@ -743,11 +749,11 @@
 
                 if (t->t_door != NULL) {
                         kmem_free(t->t_door, sizeof (door_data_t));
                         t->t_door = NULL;
                 }
-                lwp_ctmpl_clear(ttolwp(t));
+                lwp_ctmpl_clear(ttolwp(t), B_FALSE);
 
                 /*
                  * Remove the thread from the all threads list.
                  * We need to hold pidlock for this.
                  */

@@ -780,10 +786,13 @@
 
 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,10 +807,11 @@
         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,12 +851,22 @@
         } 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;
 
-                if (getproc(&p, pid, GETPROC_USER) < 0)
+                /* 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,27 +904,31 @@
                 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, curproc,
+                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,11 +965,15 @@
         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,10 +1030,13 @@
          */
         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,13 +1100,10 @@
         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;

@@ -1151,10 +1179,22 @@
         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