From 2a4b225146353351c6af4a1904dca6ee9a433341 Mon Sep 17 00:00:00 2001 From: Jamie Gritton Date: Sat, 9 Jan 2021 21:05:06 -0800 Subject: jail: Simplify handling of prison_deref() Track the the current lock/reference state in a single variable, rather than deducing the proper prison_deref() flags from a combination of equations and hard-coded values. --- sys/kern/kern_jail.c | 324 +++++++++++++++++++++++++-------------------------- 1 file changed, 161 insertions(+), 163 deletions(-) diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 55006939a5ff..ff540875c90e 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -151,11 +151,11 @@ static void prison_racct_detach(struct prison *pr); #endif /* Flags for prison_deref */ -#define PD_DEREF 0x01 -#define PD_DEUREF 0x02 -#define PD_LOCKED 0x04 -#define PD_LIST_SLOCKED 0x08 -#define PD_LIST_XLOCKED 0x10 +#define PD_DEREF 0x01 /* Decrement pr_ref */ +#define PD_DEUREF 0x02 /* Decrement pr_uref */ +#define PD_LOCKED 0x04 /* pr_mtx is held */ +#define PD_LIST_SLOCKED 0x08 /* allprison_lock is held shared */ +#define PD_LIST_XLOCKED 0x10 /* allprison_lock is held exclusive */ /* * Parameter names corresponding to PR_* flag values. Size values are for kvm @@ -526,7 +526,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) #endif unsigned long hid; size_t namelen, onamelen, pnamelen; - int born, created, cuflags, descend, enforce, slocked; + int born, created, cuflags, descend, drflags, enforce; int error, errmsg_len, errmsg_pos; int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel; int jid, jsys, len, level; @@ -994,11 +994,12 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) jid = 0; } sx_xlock(&allprison_lock); + drflags = PD_LIST_XLOCKED; if (jid != 0) { if (jid < 0) { error = EINVAL; vfs_opterror(opts, "negative jid"); - goto done_unlock_list; + goto done_deref; } /* * See if a requested jid already exists. Keep track of @@ -1009,6 +1010,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) mtx_lock(&inspr->pr_mtx); if (inspr->pr_ref > 0) { pr = inspr; + drflags |= PD_LOCKED; inspr = NULL; } else mtx_unlock(&inspr->pr_mtx); @@ -1025,11 +1027,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) * Even creators that cannot see the jail will * get EEXIST. */ - mtx_unlock(&pr->pr_mtx); error = EEXIST; vfs_opterror(opts, "jail %d already exists", jid); - goto done_unlock_list; + goto done_deref; } if (!prison_ischild(mypr, pr)) { /* @@ -1037,15 +1038,15 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) * jail. This is true even for CREATE | UPDATE, * which normally cannot give this error. */ - mtx_unlock(&pr->pr_mtx); - pr = NULL; + error = ENOENT; + vfs_opterror(opts, "jail %d not found", jid); + goto done_deref; } else if (pr->pr_uref == 0) { if (!(flags & JAIL_DYING)) { - mtx_unlock(&pr->pr_mtx); error = ENOENT; vfs_opterror(opts, "jail %d is dying", jid); - goto done_unlock_list; + goto done_deref; } else if ((flags & JAIL_ATTACH) || (pr_flags & PR_PERSIST)) { /* @@ -1060,13 +1061,12 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) name = prison_name(mypr, pr); } } - } - if (pr == NULL) { + } else { /* Update: jid must exist. */ if (cuflags == JAIL_UPDATE) { error = ENOENT; vfs_opterror(opts, "jail %d not found", jid); - goto done_unlock_list; + goto done_deref; } } } @@ -1091,11 +1091,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) if (pr != NULL) { if (strncmp(name, ppr->pr_name, namelc - name) || ppr->pr_name[namelc - name] != '\0') { - mtx_unlock(&pr->pr_mtx); error = EINVAL; vfs_opterror(opts, "cannot change jail's parent"); - goto done_unlock_list; + goto done_deref; } } else { *namelc = '\0'; @@ -1104,7 +1103,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) error = ENOENT; vfs_opterror(opts, "jail \"%s\" not found", name); - goto done_unlock_list; + goto done_deref; } mtx_unlock(&ppr->pr_mtx); *namelc = '.'; @@ -1129,6 +1128,8 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) */ if (tpr->pr_uref > 0) { pr = tpr; + drflags |= + PD_LOCKED; break; } deadpr = tpr; @@ -1141,12 +1142,10 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) * active sibling jail. */ error = EEXIST; - if (pr != NULL) - mtx_unlock(&pr->pr_mtx); vfs_opterror(opts, "jail \"%s\" already exists", name); - goto done_unlock_list; + goto done_deref; } } } @@ -1159,11 +1158,12 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) goto name_again; } pr = deadpr; + drflags |= PD_LOCKED; } else if (cuflags == JAIL_UPDATE) { error = ENOENT; vfs_opterror(opts, "jail \"%s\" is dying", name); - goto done_unlock_list; + goto done_deref; } } /* Update: name must exist if no jid. */ @@ -1171,7 +1171,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) error = ENOENT; vfs_opterror(opts, "jail \"%s\" not found", name); - goto done_unlock_list; + goto done_deref; } } } @@ -1179,39 +1179,43 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) else if (cuflags == JAIL_UPDATE && pr == NULL) { error = ENOENT; vfs_opterror(opts, "update specified no jail"); - goto done_unlock_list; + goto done_deref; } /* If there's no prison to update, create a new one and link it in. */ - if (pr == NULL) { + created = pr == NULL; + if (created) { for (tpr = mypr; tpr != NULL; tpr = tpr->pr_parent) if (tpr->pr_childcount >= tpr->pr_childmax) { error = EPERM; vfs_opterror(opts, "prison limit exceeded"); - goto done_unlock_list; + goto done_deref; } - created = 1; mtx_lock(&ppr->pr_mtx); if (ppr->pr_ref == 0) { mtx_unlock(&ppr->pr_mtx); error = ENOENT; vfs_opterror(opts, "jail \"%s\" not found", prison_name(mypr, ppr)); - goto done_unlock_list; + goto done_deref; } ppr->pr_ref++; ppr->pr_uref++; mtx_unlock(&ppr->pr_mtx); - pr = malloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO); if (jid == 0 && (jid = get_next_prid(&inspr)) == 0) { error = EAGAIN; vfs_opterror(opts, "no available jail IDs"); - free(pr, M_PRISON); - prison_deref(ppr, - PD_DEREF | PD_DEUREF | PD_LIST_XLOCKED); - goto done_releroot; + pr = ppr; + drflags |= PD_DEREF | PD_DEUREF; + goto done_deref; } + + pr = malloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO); + LIST_INIT(&pr->pr_children); + mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF | MTX_DUPOK); + TASK_INIT(&pr->pr_task, 0, prison_complete, pr); + pr->pr_id = jid; if (inspr != NULL) TAILQ_INSERT_BEFORE(inspr, pr, pr_list); @@ -1286,10 +1290,6 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) strlcpy(pr->pr_osrelease, osrelstr, sizeof(pr->pr_osrelease)); - LIST_INIT(&pr->pr_children); - mtx_init(&pr->pr_mtx, "jail mutex", NULL, MTX_DEF | MTX_DUPOK); - TASK_INIT(&pr->pr_task, 0, prison_complete, pr); - #ifdef VIMAGE /* Allocate a new vnet if specified. */ pr->pr_vnet = (pr_flags & PR_VNET) @@ -1300,31 +1300,30 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) * Unlike other initial settings, this may return an erorr. */ error = cpuset_create_root(ppr, &pr->pr_cpuset); - if (error) { - prison_deref(pr, PD_LIST_XLOCKED); - goto done_releroot; - } + if (error) + goto done_deref; mtx_lock(&pr->pr_mtx); + drflags |= PD_LOCKED; /* * New prisons do not yet have a reference, because we do not * want others to see the incomplete prison once the * allprison_lock is downgraded. */ } else { - created = 0; /* * Grab a reference for existing prisons, to ensure they * continue to exist for the duration of the call. */ pr->pr_ref++; + drflags |= PD_DEREF; #if defined(VIMAGE) && (defined(INET) || defined(INET6)) if ((pr->pr_flags & PR_VNET) && (ch_flags & (PR_IP4_USER | PR_IP6_USER))) { error = EINVAL; vfs_opterror(opts, "vnet jails cannot have IP address restrictions"); - goto done_deref_locked; + goto done_deref; } #endif #ifdef INET @@ -1332,7 +1331,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) error = EINVAL; vfs_opterror(opts, "ip4 cannot be changed after creation"); - goto done_deref_locked; + goto done_deref; } #endif #ifdef INET6 @@ -1340,7 +1339,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) error = EINVAL; vfs_opterror(opts, "ip6 cannot be changed after creation"); - goto done_deref_locked; + goto done_deref; } #endif } @@ -1349,19 +1348,19 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) if (gotslevel) { if (slevel < ppr->pr_securelevel) { error = EPERM; - goto done_deref_locked; + goto done_deref; } } if (gotchildmax) { if (childmax >= ppr->pr_childmax) { error = EPERM; - goto done_deref_locked; + goto done_deref; } } if (gotenforce) { if (enforce < ppr->pr_enforce_statfs) { error = EPERM; - goto done_deref_locked; + goto done_deref; } } if (gotrsnum) { @@ -1370,7 +1369,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) */ if (rsnum < 0 || rsnum > 65535) { error = EINVAL; - goto done_deref_locked; + goto done_deref; } /* * Nested jails always inherit parent's devfs ruleset @@ -1378,7 +1377,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) if (jailed(td->td_ucred)) { if (rsnum > 0 && rsnum != ppr->pr_devfs_rsnum) { error = EPERM; - goto done_deref_locked; + goto done_deref; } else rsnum = ppr->pr_devfs_rsnum; } @@ -1397,7 +1396,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) break; if (ij == ppr->pr_ip4s) { error = EPERM; - goto done_deref_locked; + goto done_deref; } if (ip4s > 1) { for (ii = ij = 1; ii < ip4s; ii++) { @@ -1413,7 +1412,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) } if (ij == ppr->pr_ip4s) { error = EPERM; - goto done_deref_locked; + goto done_deref; } } } @@ -1451,7 +1450,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) error = EADDRINUSE; vfs_opterror(opts, "IPv4 addresses clash"); - goto done_deref_locked; + goto done_deref; } } } @@ -1470,7 +1469,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) break; if (ij == ppr->pr_ip6s) { error = EPERM; - goto done_deref_locked; + goto done_deref; } if (ip6s > 1) { for (ii = ij = 1; ii < ip6s; ii++) { @@ -1486,7 +1485,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) } if (ij == ppr->pr_ip6s) { error = EPERM; - goto done_deref_locked; + goto done_deref; } } } @@ -1519,7 +1518,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) error = EADDRINUSE; vfs_opterror(opts, "IPv6 addresses clash"); - goto done_deref_locked; + goto done_deref; } } } @@ -1538,7 +1537,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) error = EINVAL; vfs_opterror(opts, "name cannot be numeric (unless it is the jid)"); - goto done_deref_locked; + goto done_deref; } /* * Make sure the name isn't too long for the prison or its @@ -1549,20 +1548,20 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) namelen = strlen(namelc); if (pnamelen + namelen + 1 > sizeof(pr->pr_name)) { error = ENAMETOOLONG; - goto done_deref_locked; + goto done_deref; } FOREACH_PRISON_DESCENDANT(pr, tpr, descend) { if (strlen(tpr->pr_name) + (namelen - onamelen) >= sizeof(pr->pr_name)) { error = ENAMETOOLONG; - goto done_deref_locked; + goto done_deref; } } } pr_allow_diff = pr_allow & ~ppr->pr_allow; if (pr_allow_diff & ~PR_ALLOW_DIFFERENCES) { error = EPERM; - goto done_deref_locked; + goto done_deref; } /* @@ -1571,21 +1570,19 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) * as allprison_lock remains xlocked. */ mtx_unlock(&pr->pr_mtx); + drflags &= ~PD_LOCKED; error = osd_jail_call(pr, PR_METHOD_CHECK, opts); - if (error != 0) { - prison_deref(pr, created - ? PD_LIST_XLOCKED - : PD_DEREF | PD_LIST_XLOCKED); - goto done_releroot; - } + if (error != 0) + goto done_deref; mtx_lock(&pr->pr_mtx); + drflags |= PD_LOCKED; /* At this point, all valid parameters should have been noted. */ TAILQ_FOREACH(opt, opts, link) { if (!opt->seen && strcmp(opt->name, "errmsg")) { error = EINVAL; vfs_opterror(opts, "unknown parameter: %s", opt->name); - goto done_deref_locked; + goto done_deref; } } @@ -1679,6 +1676,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) /* Try to keep a real-rooted full pathname. */ strlcpy(pr->pr_path, path, sizeof(pr->pr_path)); pr->pr_root = root; + root = NULL; } if (PR_HOST & ch_flags & ~pr_flags) { if (pr->pr_flags & PR_HOST) { @@ -1749,6 +1747,7 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) } pr->pr_flags = (pr->pr_flags & ~ch_flags) | pr_flags; mtx_unlock(&pr->pr_mtx); + drflags &= ~PD_LOCKED; #ifdef RACCT if (racct_enable && created) @@ -1807,45 +1806,41 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) /* Let the modules do their work. */ sx_downgrade(&allprison_lock); + drflags = (drflags & ~PD_LIST_XLOCKED) | PD_LIST_SLOCKED; if (born) { error = osd_jail_call(pr, PR_METHOD_CREATE, opts); if (error) { (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL); - prison_deref(pr, created - ? PD_LIST_SLOCKED - : PD_DEREF | PD_LIST_SLOCKED); - goto done_errmsg; + goto done_deref; } } error = osd_jail_call(pr, PR_METHOD_SET, opts); if (error) { if (born) (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL); - prison_deref(pr, created - ? PD_LIST_SLOCKED - : PD_DEREF | PD_LIST_SLOCKED); - goto done_errmsg; + goto done_deref; } /* Attach this process to the prison if requested. */ - slocked = PD_LIST_SLOCKED; if (flags & JAIL_ATTACH) { mtx_lock(&pr->pr_mtx); error = do_jail_attach(td, pr); - slocked = 0; + drflags &= ~PD_LIST_SLOCKED; if (error) { + if (created) { + /* do_jail_attach has removed the prison. */ + pr = NULL; + } vfs_opterror(opts, "attach failed"); - if (!created) - prison_deref(pr, PD_DEREF); - goto done_errmsg; + goto done_deref; } } #ifdef RACCT if (racct_enable && !created) { - if (slocked) { + if (drflags & PD_LIST_SLOCKED) { sx_sunlock(&allprison_lock); - slocked = 0; + drflags &= ~PD_LIST_SLOCKED; } prison_racct_modify(pr); } @@ -1853,39 +1848,36 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags) td->td_retval[0] = pr->pr_id; - /* - * Now that it is all there, drop the temporary reference from existing - * prisons. Or add a reference to newly created persistent prisons - * (which was not done earlier so that the prison would not be publicly - * visible). - */ - if (!created) - prison_deref(pr, PD_DEREF | slocked); - else { + if (created) { + /* + * Add a reference to newly created persistent prisons + * (which was not done earlier so that the prison would + * not be publicly visible). + */ if (pr_flags & PR_PERSIST) { mtx_lock(&pr->pr_mtx); + drflags |= PD_LOCKED; pr->pr_ref++; pr->pr_uref++; - mtx_unlock(&pr->pr_mtx); + } else { + /* Non-persistent jails need no further changes. */ + pr = NULL; } - if (slocked) - sx_sunlock(&allprison_lock); } - goto done_free; - - done_deref_locked: - prison_deref(pr, created - ? PD_LOCKED | PD_LIST_XLOCKED - : PD_DEREF | PD_LOCKED | PD_LIST_XLOCKED); - goto done_releroot; - done_unlock_list: - sx_xunlock(&allprison_lock); - done_releroot: + done_deref: + /* Release any temporary prison holds and/or locks. */ + if (pr != NULL) + prison_deref(pr, drflags); + else if (drflags & PD_LIST_SLOCKED) + sx_sunlock(&allprison_lock); + else if (drflags & PD_LIST_XLOCKED) + sx_xunlock(&allprison_lock); if (root != NULL) vrele(root); done_errmsg: if (error) { + /* Write the error message back to userspace. */ if (vfs_getopt(opts, "errmsg", (void **)&errmsg, &errmsg_len) == 0 && errmsg_len > 0) { errmsg_pos = 2 * vfs_getopt_pos(opts, "errmsg") + 1; @@ -2013,7 +2005,7 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) struct vfsopt *opt; struct vfsoptlist *opts; char *errmsg, *name; - int error, errmsg_len, errmsg_pos, i, jid, len, locked, pos; + int drflags, error, errmsg_len, errmsg_pos, i, jid, len, pos; unsigned f; if (flags & ~JAIL_GET_MASK) @@ -2025,11 +2017,13 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) return (error); errmsg_pos = vfs_getopt_pos(opts, "errmsg"); mypr = td->td_ucred->cr_prison; + pr = NULL; /* * Find the prison specified by one of: lastjid, jid, name. */ sx_slock(&allprison_lock); + drflags = PD_LIST_SLOCKED; error = vfs_copyopt(opts, "lastjid", &jid, sizeof(jid)); if (error == 0) { TAILQ_FOREACH(pr, &allprison, pr_list) { @@ -2041,117 +2035,119 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) mtx_unlock(&pr->pr_mtx); } } - if (pr != NULL) + if (pr != NULL) { + drflags |= PD_LOCKED; goto found_prison; + } error = ENOENT; vfs_opterror(opts, "no jail after %d", jid); - goto done_unlock_list; + goto done; } else if (error != ENOENT) - goto done_unlock_list; + goto done; error = vfs_copyopt(opts, "jid", &jid, sizeof(jid)); if (error == 0) { if (jid != 0) { pr = prison_find_child(mypr, jid); if (pr != NULL) { + drflags |= PD_LOCKED; if (pr->pr_uref == 0 && !(flags & JAIL_DYING)) { - mtx_unlock(&pr->pr_mtx); error = ENOENT; vfs_opterror(opts, "jail %d is dying", jid); - goto done_unlock_list; + goto done; } goto found_prison; } error = ENOENT; vfs_opterror(opts, "jail %d not found", jid); - goto done_unlock_list; + goto done; } } else if (error != ENOENT) - goto done_unlock_list; + goto done; error = vfs_getopt(opts, "name", (void **)&name, &len); if (error == 0) { if (len == 0 || name[len - 1] != '\0') { error = EINVAL; - goto done_unlock_list; + goto done; } pr = prison_find_name(mypr, name); if (pr != NULL) { + drflags |= PD_LOCKED; if (pr->pr_uref == 0 && !(flags & JAIL_DYING)) { - mtx_unlock(&pr->pr_mtx); error = ENOENT; vfs_opterror(opts, "jail \"%s\" is dying", name); - goto done_unlock_list; + goto done; } goto found_prison; } error = ENOENT; vfs_opterror(opts, "jail \"%s\" not found", name); - goto done_unlock_list; + goto done; } else if (error != ENOENT) - goto done_unlock_list; + goto done; vfs_opterror(opts, "no jail specified"); error = ENOENT; - goto done_unlock_list; + goto done; found_prison: /* Get the parameters of the prison. */ pr->pr_ref++; - locked = PD_LOCKED; + drflags |= PD_DEREF; td->td_retval[0] = pr->pr_id; error = vfs_setopt(opts, "jid", &pr->pr_id, sizeof(pr->pr_id)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; i = (pr->pr_parent == mypr) ? 0 : pr->pr_parent->pr_id; error = vfs_setopt(opts, "parent", &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopts(opts, "name", prison_name(mypr, pr)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopt(opts, "cpuset.id", &pr->pr_cpuset->cs_id, sizeof(pr->pr_cpuset->cs_id)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopts(opts, "path", prison_path(mypr, pr)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; #ifdef INET error = vfs_setopt_part(opts, "ip4.addr", pr->pr_ip4, pr->pr_ip4s * sizeof(*pr->pr_ip4)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; #endif #ifdef INET6 error = vfs_setopt_part(opts, "ip6.addr", pr->pr_ip6, pr->pr_ip6s * sizeof(*pr->pr_ip6)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; #endif error = vfs_setopt(opts, "securelevel", &pr->pr_securelevel, sizeof(pr->pr_securelevel)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopt(opts, "children.cur", &pr->pr_childcount, sizeof(pr->pr_childcount)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopt(opts, "children.max", &pr->pr_childmax, sizeof(pr->pr_childmax)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopts(opts, "host.hostname", pr->pr_hostname); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopts(opts, "host.domainname", pr->pr_domainname); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopts(opts, "host.hostuuid", pr->pr_hostuuid); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; #ifdef COMPAT_FREEBSD32 if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) { uint32_t hid32 = pr->pr_hostid; @@ -2162,26 +2158,26 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) error = vfs_setopt(opts, "host.hostid", &pr->pr_hostid, sizeof(pr->pr_hostid)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopt(opts, "enforce_statfs", &pr->pr_enforce_statfs, sizeof(pr->pr_enforce_statfs)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopt(opts, "devfs_ruleset", &pr->pr_devfs_rsnum, sizeof(pr->pr_devfs_rsnum)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; for (bf = pr_flag_bool; bf < pr_flag_bool + nitems(pr_flag_bool); bf++) { i = (pr->pr_flags & bf->flag) ? 1 : 0; error = vfs_setopt(opts, bf->name, &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; i = !i; error = vfs_setopt(opts, bf->noname, &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; } for (jsf = pr_flag_jailsys; jsf < pr_flag_jailsys + nitems(pr_flag_jailsys); @@ -2192,7 +2188,7 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) : JAIL_SYS_INHERIT; error = vfs_setopt(opts, jsf->name, &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; } for (bf = pr_flag_allow; bf < pr_flag_allow + nitems(pr_flag_allow) && @@ -2201,42 +2197,44 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) i = (pr->pr_allow & bf->flag) ? 1 : 0; error = vfs_setopt(opts, bf->name, &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; i = !i; error = vfs_setopt(opts, bf->noname, &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; } i = (pr->pr_uref == 0); error = vfs_setopt(opts, "dying", &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; i = !i; error = vfs_setopt(opts, "nodying", &i, sizeof(i)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopt(opts, "osreldate", &pr->pr_osreldate, sizeof(pr->pr_osreldate)); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; error = vfs_setopts(opts, "osrelease", pr->pr_osrelease); if (error != 0 && error != ENOENT) - goto done_deref; + goto done; /* Get the module parameters. */ mtx_unlock(&pr->pr_mtx); - locked = 0; + drflags &= ~PD_LOCKED; error = osd_jail_call(pr, PR_METHOD_GET, opts); if (error) - goto done_deref; - prison_deref(pr, PD_DEREF | PD_LIST_SLOCKED); + goto done; + prison_deref(pr, drflags); + pr = NULL; + drflags = 0; /* By now, all parameters should have been noted. */ TAILQ_FOREACH(opt, opts, link) { if (!opt->seen && strcmp(opt->name, "errmsg")) { error = EINVAL; vfs_opterror(opts, "unknown parameter: %s", opt->name); - goto done_errmsg; + goto done; } } @@ -2261,16 +2259,15 @@ kern_jail_get(struct thread *td, struct uio *optuio, int flags) } } } - goto done_errmsg; - done_deref: - prison_deref(pr, locked | PD_DEREF | PD_LIST_SLOCKED); - goto done_errmsg; - - done_unlock_list: - sx_sunlock(&allprison_lock); - done_errmsg: + done: + /* Release any temporary prison holds and/or locks. */ + if (pr != NULL) + prison_deref(pr, drflags); + else if (drflags & PD_LIST_SLOCKED) + sx_sunlock(&allprison_lock); if (error && errmsg_pos >= 0) { + /* Write the error message back to userspace. */ vfs_getopt(opts, "errmsg", (void **)&errmsg, &errmsg_len); errmsg_pos = 2 * errmsg_pos + 1; if (errmsg_len > 0) { @@ -2347,13 +2344,14 @@ static void prison_remove_one(struct prison *pr) { struct proc *p; - int deuref; + int drflags; + + drflags = PD_DEREF | PD_LOCKED | PD_LIST_XLOCKED; /* If the prison was persistent, it is not anymore. */ - deuref = 0; if (pr->pr_flags & PR_PERSIST) { pr->pr_ref--; - deuref = PD_DEUREF; + drflags |= PD_DEUREF; pr->pr_flags &= ~PR_PERSIST; } @@ -2364,13 +2362,13 @@ prison_remove_one(struct prison *pr) KASSERT(pr->pr_ref > 0, ("prison_remove_one removing a dead prison (jid=%d)", pr->pr_id)); if (pr->pr_ref == 1) { - prison_deref(pr, - deuref | PD_DEREF | PD_LOCKED | PD_LIST_XLOCKED); + prison_deref(pr, drflags); return; } mtx_unlock(&pr->pr_mtx); sx_xunlock(&allprison_lock); + drflags &= ~(PD_LOCKED | PD_LIST_XLOCKED); /* * Kill all processes unfortunate enough to be attached to this prison. */ @@ -2384,7 +2382,7 @@ prison_remove_one(struct prison *pr) } sx_sunlock(&allproc_lock); /* Remove the temporary reference added by jail_remove. */ - prison_deref(pr, deuref | PD_DEREF); + prison_deref(pr, drflags); } /* -- cgit v1.2.3