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