Print this page
OS-4818 contract template disappears on exec
OS-5311 docker init children not always in the correct contract
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
OS-4937 lxbrand ptracer count updates can race
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
OS-4534 lwp_exit P_PR_LOCK assertion failure
OS-4188 NULL dereference in lwp_hash_in
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>
        
@@ -23,11 +23,11 @@
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/sysmacros.h>
@@ -55,10 +55,12 @@
 #include <sys/lwp_upimutex_impl.h>
 #include <sys/cpupart.h>
 #include <sys/lgrp.h>
 #include <sys/rctl.h>
 #include <sys/contract_impl.h>
+#include <sys/contract/process.h>
+#include <sys/contract/process_impl.h>
 #include <sys/cpc_impl.h>
 #include <sys/sdt.h>
 #include <sys/cmn_err.h>
 #include <sys/brand.h>
 #include <sys/cyclic.h>
@@ -113,11 +115,11 @@
         tidhash_t *old_hash = NULL;
         uint_t old_hashsz = 0;
         ret_tidhash_t *ret_tidhash = NULL;
         int i;
         int rctlfail = 0;
-        boolean_t branded = 0;
+        void *brand_data = NULL;
         struct ctxop *ctx = NULL;
 
         ASSERT(cid != sysdccid);        /* system threads must start in SYS */
 
         ASSERT(p != &p0);               /* No new LWPs in p0. */
@@ -281,11 +283,24 @@
         /*
          * Allocate an lwp directory entry for the new lwp.
          */
         lep = kmem_zalloc(sizeof (*lep), KM_SLEEP);
 
+        /*
+         * If necessary, speculatively allocate lwp brand data.  This is done
+         * ahead of time so p_lock need not be dropped during lwp branding.
+         */
+        if (PROC_IS_BRANDED(p) && BROP(p)->b_lwpdata_alloc != NULL) {
+                if ((brand_data = BROP(p)->b_lwpdata_alloc(p)) == NULL) {
         mutex_enter(&p->p_lock);
+                        err = 1;
+                        atomic_inc_32(&p->p_zone->zone_ffmisc);
+                        goto error;
+                }
+        }
+
+        mutex_enter(&p->p_lock);
 grow:
         /*
          * Grow the lwp (thread) directory and lwpid hash table if necessary.
          * A note on the growth algorithm:
          *      The new lwp directory size is computed as:
@@ -628,22 +643,10 @@
                         if ((p->p_flag & SLWPWRAP) == 0)
                                 break;
                 } while (lwp_hash_lookup(p, t->t_tid) != NULL);
         }
 
-        /*
-         * If this is a branded process, let the brand do any necessary lwp
-         * initialization.
-         */
-        if (PROC_IS_BRANDED(p)) {
-                if (BROP(p)->b_initlwp(lwp)) {
-                        err = 1;
-                        atomic_inc_32(&p->p_zone->zone_ffmisc);
-                        goto error;
-                }
-                branded = 1;
-        }
 
         if (t->t_tid == 1) {
                 kpreempt_disable();
                 ASSERT(t->t_lpl != NULL);
                 p->p_t1_lgrpid = t->t_lpl->lpl_lgrpid;
@@ -652,11 +655,10 @@
                     p->p_tr_lgrpid != p->p_t1_lgrpid) {
                         lgrp_update_trthr_migrations(1);
                 }
         }
 
-        p->p_lwpcnt++;
         t->t_waitfor = -1;
 
         /*
          * Turn microstate accounting on for thread if on for process.
          */
@@ -694,12 +696,31 @@
          */
         t->t_pre_sys = 1;
         t->t_post_sys = 1;
 
         /*
+         * Perform lwp branding
+         *
+         * The b_initlwp hook is _not_ allowed to drop p->p_lock as it must be
+         * continuously held between when the tidhash is sized and when the lwp
+         * is inserted into it.  Operations requiring p->p_lock to be
+         * temporarily dropped can be performed in b_initlwp_post.
+         */
+        if (PROC_IS_BRANDED(p)) {
+                BROP(p)->b_initlwp(lwp, brand_data);
+                /*
+                 * The b_initlwp hook is expected to consume any preallocated
+                 * brand_data in a way that prepares it for deallocation by the
+                 * b_freelwp hook.
+                 */
+                brand_data = NULL;
+        }
+
+        /*
          * Insert the new thread into the list of all threads.
          */
+        p->p_lwpcnt++;
         if ((tx = p->p_tlist) == NULL) {
                 t->t_back = t;
                 t->t_forw = t;
                 p->p_tlist = t;
         } else {
@@ -716,10 +737,17 @@
         lep->le_thread = t;
         lep->le_lwpid = t->t_tid;
         lep->le_start = t->t_start;
         lwp_hash_in(p, lep, p->p_tidhash, p->p_tidhash_sz, 1);
 
+        /*
+         * Complete lwp branding
+         */
+        if (PROC_IS_BRANDED(p) && BROP(p)->b_initlwp_post != NULL) {
+                BROP(p)->b_initlwp_post(lwp);
+        }
+
         if (state == TS_RUN) {
                 /*
                  * We set the new lwp running immediately.
                  */
                 t->t_proc_flag &= ~TP_HOLDLWP;
@@ -751,12 +779,13 @@
                 p->p_zone->zone_nlwps--;
                 mutex_exit(&p->p_zone->zone_nlwps_lock);
                 if (cid != NOCLASS && bufp != NULL)
                         CL_FREE(cid, bufp);
 
-                if (branded)
-                        BROP(p)->b_freelwp(lwp);
+                if (brand_data != NULL) {
+                        BROP(p)->b_lwpdata_free(brand_data);
+                }
 
                 mutex_exit(&p->p_lock);
                 t->t_state = TS_FREE;
                 thread_rele(t);
 
@@ -825,35 +854,66 @@
 lwp_ctmpl_copy(klwp_t *dst, klwp_t *src)
 {
         int i;
 
         for (i = 0; i < ct_ntypes; i++) {
-                dst->lwp_ct_active[i] = ctmpl_dup(src->lwp_ct_active[i]);
+                ct_template_t *tmpl = src->lwp_ct_active[i];
+
+                /*
+                 * If the process contract template is setup to be preserved
+                 * across exec, then if we're forking, perform an implicit
+                 * template_clear now. This ensures that future children of
+                 * this child will remain in the same contract unless they're
+                 * explicitly setup differently. We know we're forking if the
+                 * two LWPs belong to different processes.
+                 */
+                if (i == CTT_PROCESS && tmpl != NULL) {
+                        ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+                        if (dst->lwp_procp != src->lwp_procp &&
+                            (ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+                                tmpl = NULL;
+                }
+
+                dst->lwp_ct_active[i] = ctmpl_dup(tmpl);
                 dst->lwp_ct_latest[i] = NULL;
+
         }
 }
 
 /*
  * Clear an LWP's contract template state.
  */
 void
-lwp_ctmpl_clear(klwp_t *lwp)
+lwp_ctmpl_clear(klwp_t *lwp, boolean_t is_exec)
 {
         ct_template_t *tmpl;
         int i;
 
         for (i = 0; i < ct_ntypes; i++) {
-                if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
-                        ctmpl_free(tmpl);
-                        lwp->lwp_ct_active[i] = NULL;
-                }
-
                 if (lwp->lwp_ct_latest[i] != NULL) {
                         contract_rele(lwp->lwp_ct_latest[i]);
                         lwp->lwp_ct_latest[i] = NULL;
                 }
+
+                if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
+                        /*
+                         * If we're exec-ing a new program and the process
+                         * contract template is setup to be preserved across
+                         * exec, then don't clear it.
+                         */
+                        if (is_exec && i == CTT_PROCESS) {
+                                ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+                                if ((ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+                                        continue;
         }
+
+                        ctmpl_free(tmpl);
+                        lwp->lwp_ct_active[i] = NULL;
+                }
+        }
 }
 
 /*
  * Individual lwp exit.
  * If this is the last lwp, exit the whole process.
@@ -889,17 +949,10 @@
                 schedctl_lwp_cleanup(t);
 
         if (t->t_upimutex != NULL)
                 upimutex_cleanup();
 
-        /*
-         * Perform any brand specific exit processing, then release any
-         * brand data associated with the lwp
-         */
-        if (PROC_IS_BRANDED(p))
-                BROP(p)->b_lwpexit(lwp);
-
         lwp_pcb_exit();
 
         mutex_enter(&p->p_lock);
         lwp_cleanup();
 
@@ -939,10 +992,22 @@
         }
 
         DTRACE_PROC(lwp__exit);
 
         /*
+         * Perform any brand specific exit processing, then release any
+         * brand data associated with the lwp
+         */
+        if (PROC_IS_BRANDED(p)) {
+                mutex_exit(&p->p_lock);
+                BROP(p)->b_lwpexit(lwp);
+                BROP(p)->b_freelwp(lwp);
+                mutex_enter(&p->p_lock);
+                prbarrier(p);
+        }
+
+        /*
          * If the lwp is a detached lwp or if the process is exiting,
          * remove (lwp_hash_out()) the lwp from the lwp directory.
          * Otherwise null out the lwp's le_thread pointer in the lwp
          * directory so that other threads will see it as a zombie lwp.
          */
@@ -1099,11 +1164,11 @@
         if (t->t_tid == 1) {
                 p->p_t1_lgrpid = LGRP_NONE;
         }
         kpreempt_enable();
 
-        lwp_ctmpl_clear(ttolwp(t));
+        lwp_ctmpl_clear(ttolwp(t), B_FALSE);
 }
 
 int
 lwp_suspend(kthread_t *t)
 {