diff --git a/kern/kern_jail.c b/kern/kern_jail_28419.c --- a/kern/kern_jail.c +++ b/kern/kern_jail_28419.c @@ -140,6 +140,8 @@ static int do_jail_attach(struct thread *td, struct prison *pr); static void prison_complete(void *context, int pending); static void prison_deref(struct prison *pr, int flags); +static int prison_lock_xlock(struct prison *pr, int flags); +static void prison_free_not_last(struct prison *pr); static void prison_set_allow_locked(struct prison *pr, unsigned flag, int enable); static char *prison_path(struct prison *pr1, struct prison *pr2); @@ -1006,18 +1008,15 @@ * where it can be inserted later. */ TAILQ_FOREACH(inspr, &allprison, pr_list) { - if (inspr->pr_id == jid) { - mtx_lock(&inspr->pr_mtx); - if (prison_isvalid(inspr)) { - pr = inspr; - drflags |= PD_LOCKED; - inspr = NULL; - } else - mtx_unlock(&inspr->pr_mtx); - break; - } + if (inspr->pr_id < jid) + continue; if (inspr->pr_id > jid) break; + pr = inspr; + mtx_lock(&pr->pr_mtx); + drflags |= PD_LOCKED; + inspr = NULL; + break; } if (pr != NULL) { ppr = pr->pr_parent; @@ -1032,7 +1031,7 @@ jid); goto done_deref; } - if (!prison_ischild(mypr, pr)) { + if (!prison_isvalid(pr) || !prison_ischild(mypr, pr)) { /* * Updaters get ENOENT if they cannot see the * jail. This is true even for CREATE | UPDATE, @@ -1047,7 +1046,8 @@ vfs_opterror(opts, "jail %d is dying", jid); goto done_deref; - } else if ((flags & JAIL_ATTACH) || + } + if ((flags & JAIL_ATTACH) || (pr_flags & PR_PERSIST)) { /* * A dying jail might be resurrected @@ -1126,7 +1126,6 @@ FOREACH_PRISON_CHILD(ppr, tpr) { if (tpr != pr && !strcmp(tpr->pr_name + pnamelen, namelc)) { - mtx_lock(&tpr->pr_mtx); if (prison_isalive(tpr)) { if (pr == NULL && cuflags != JAIL_CREATE) { @@ -1135,6 +1134,7 @@ * for updates. */ pr = tpr; + mtx_lock(&tpr->pr_mtx); drflags |= PD_LOCKED; break; } @@ -1144,7 +1144,6 @@ * active sibling jail. */ error = EEXIST; - mtx_unlock(&tpr->pr_mtx); vfs_opterror(opts, "jail \"%s\" already exists", name); @@ -1154,18 +1153,15 @@ cuflags != JAIL_CREATE && prison_isvalid(tpr)) deadpr = tpr; - mtx_unlock(&tpr->pr_mtx); } } /* If no active jail is found, use a dying one. */ if (deadpr != NULL && pr == NULL) { if (flags & JAIL_DYING) { - mtx_lock(&deadpr->pr_mtx); - if (!prison_isvalid(deadpr)) { - mtx_unlock(&deadpr->pr_mtx); + if (!prison_isvalid(deadpr)) goto name_again; - } pr = deadpr; + mtx_lock(&pr->pr_mtx); drflags |= PD_LOCKED; } else if (cuflags == JAIL_UPDATE) { error = ENOENT; @@ -1199,19 +1195,11 @@ vfs_opterror(opts, "prison limit exceeded"); goto done_deref; } - mtx_lock(&ppr->pr_mtx); - if (!prison_isvalid(ppr)) { - mtx_unlock(&ppr->pr_mtx); - error = ENOENT; - vfs_opterror(opts, "jail \"%s\" not found", - prison_name(mypr, ppr)); - goto done_deref; - } prison_hold(ppr); - if (refcount_acquire(&ppr->pr_uref)) - mtx_unlock(&ppr->pr_mtx); - else { + if (!refcount_acquire_if_not_zero(&ppr->pr_uref)) { /* This brings the parent back to life. */ + mtx_lock(&ppr->pr_mtx); + refcount_acquire(&ppr->pr_uref); mtx_unlock(&ppr->pr_mtx); error = osd_jail_call(ppr, PR_METHOD_CREATE, opts); if (error) { @@ -1219,7 +1207,7 @@ drflags |= PD_DEREF | PD_DEUREF; goto done_deref; } - } + } if (jid == 0 && (jid = get_next_prid(&inspr)) == 0) { error = EAGAIN; @@ -1230,6 +1218,8 @@ } pr = malloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO); + refcount_init(&pr->pr_ref, 0); + refcount_init(&pr->pr_uref, 0); 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); @@ -1452,7 +1442,7 @@ #ifdef VIMAGE (tpr != tppr && (tpr->pr_flags & PR_VNET)) || #endif - refcount_load(&tpr->pr_uref) == 0) { + !prison_isalive(tpr)) { descend = 0; continue; } @@ -1520,7 +1510,7 @@ #ifdef VIMAGE (tpr != tppr && (tpr->pr_flags & PR_VNET)) || #endif - refcount_load(&tpr->pr_uref) == 0) { + !prison_isalive(tpr)) { descend = 0; continue; } @@ -1759,8 +1749,8 @@ prison_hold(pr); refcount_acquire(&pr->pr_uref); } else { - refcount_release(&pr->pr_ref); drflags |= PD_DEUREF; + prison_free_not_last(pr); } } pr->pr_flags = (pr->pr_flags & ~ch_flags) | pr_flags; @@ -1873,8 +1863,7 @@ * not be publicly visible). */ if (pr_flags & PR_PERSIST) { - mtx_lock(&pr->pr_mtx); - drflags |= PD_LOCKED; + drflags = prison_lock_xlock(pr, drflags); refcount_acquire(&pr->pr_ref); refcount_acquire(&pr->pr_uref); } else { @@ -1951,13 +1940,8 @@ TAILQ_FOREACH(inspr, &allprison, pr_list) { if (inspr->pr_id < jid) continue; - if (inspr->pr_id > jid || - refcount_load(&inspr->pr_ref) == 0) { - /* - * Found an opening. This may be a gap - * in the list, or a dead jail with the - * same ID. - */ + if (inspr->pr_id > jid) { + /* Found an opening. */ maxid = 0; break; } @@ -2046,18 +2030,14 @@ error = vfs_copyopt(opts, "lastjid", &jid, sizeof(jid)); if (error == 0) { TAILQ_FOREACH(pr, &allprison, pr_list) { - if (pr->pr_id > jid && prison_ischild(mypr, pr)) { + if (pr->pr_id > jid && ((flags & JAIL_DYING) + ? prison_isvalid(pr) : prison_isalive(pr)) && + prison_ischild(mypr, pr)) { mtx_lock(&pr->pr_mtx); - if ((flags & JAIL_DYING) - ? prison_isvalid(pr) : prison_isalive(pr)) - break; - mtx_unlock(&pr->pr_mtx); + drflags |= PD_LOCKED; + goto found_prison; } } - if (pr != NULL) { - drflags |= PD_LOCKED; - goto found_prison; - } error = ENOENT; vfs_opterror(opts, "no jail after %d", jid); goto done; @@ -2333,15 +2313,11 @@ mtx_unlock(&pr->pr_mtx); lpr = NULL; FOREACH_PRISON_DESCENDANT(pr, cpr, descend) { - mtx_lock(&cpr->pr_mtx); if (prison_isvalid(cpr)) { tpr = cpr; - prison_hold(cpr); - } else { - /* Already removed - do not do it again. */ + prison_hold(tpr); + } else tpr = NULL; - } - mtx_unlock(&cpr->pr_mtx); if (lpr != NULL) { mtx_lock(&lpr->pr_mtx); prison_remove_one(lpr); @@ -2370,7 +2346,7 @@ /* If the prison was persistent, it is not anymore. */ if (pr->pr_flags & PR_PERSIST) { - refcount_release(&pr->pr_ref); + prison_free_not_last(pr); drflags |= PD_DEUREF; pr->pr_flags &= ~PR_PERSIST; } @@ -2421,14 +2397,7 @@ if (error) return (error); - /* - * Start with exclusive hold on allprison_lock to ensure that a possible - * PR_METHOD_REMOVE call isn't concurrent with jail_set or jail_remove. - * But then immediately downgrade it since we don't need to stop - * readers. - */ - sx_xlock(&allprison_lock); - sx_downgrade(&allprison_lock); + sx_slock(&allprison_lock); pr = prison_find_child(td->td_ucred->cr_prison, uap->jid); if (pr == NULL) { sx_sunlock(&allprison_lock); @@ -2450,7 +2419,7 @@ { struct proc *p; struct ucred *newcred, *oldcred; - int error; + int drflags, error; /* * XXX: Note that there is a slight race here if two threads @@ -2460,17 +2429,27 @@ * a process root from one prison, but attached to the jail * of another. */ + drflags = PD_DEREF | PD_DEUREF | PD_LOCKED | PD_LIST_SLOCKED; + if (!prison_isalive(pr)) { + /* + * This will be the first reference, so allprison_lock + * needs an exclusive hold. + */ + drflags = prison_lock_xlock(pr, drflags); + } refcount_acquire(&pr->pr_ref); refcount_acquire(&pr->pr_uref); mtx_unlock(&pr->pr_mtx); + drflags &= ~PD_LOCKED; /* Let modules do whatever they need to prepare for attaching. */ error = osd_jail_call(pr, PR_METHOD_ATTACH, td); if (error) { - prison_deref(pr, PD_DEREF | PD_DEUREF | PD_LIST_SLOCKED); + prison_deref(pr, drflags); return (error); } - sx_sunlock(&allprison_lock); + sx_unlock(&allprison_lock); + drflags &= ~(PD_LIST_SLOCKED | PD_LIST_XLOCKED); /* * Reparent the newly attached process to this jail. @@ -2506,7 +2485,7 @@ rctl_proc_ucred_changed(p, newcred); crfree(newcred); #endif - prison_deref(oldcred->cr_prison, PD_DEREF | PD_DEUREF); + prison_deref(oldcred->cr_prison, drflags); crfree(oldcred); return (0); @@ -2515,8 +2494,9 @@ e_revert_osd: /* Tell modules this thread is still in its old jail after all. */ sx_slock(&allprison_lock); + drflags |= PD_LIST_SLOCKED; (void)osd_jail_call(td->td_ucred->cr_prison, PR_METHOD_ATTACH, td); - prison_deref(pr, PD_DEREF | PD_DEUREF | PD_LIST_SLOCKED); + prison_deref(pr, drflags); return (error); } @@ -2531,14 +2511,14 @@ sx_assert(&allprison_lock, SX_LOCKED); TAILQ_FOREACH(pr, &allprison, pr_list) { if (pr->pr_id == prid) { - mtx_lock(&pr->pr_mtx); - if (prison_isvalid(pr)) + if (prison_isvalid(pr)) { + mtx_lock(&pr->pr_mtx); return (pr); + } /* * Any active prison with the same ID would have * been inserted before a dead one. */ - mtx_unlock(&pr->pr_mtx); break; } if (pr->pr_id > prid) @@ -2559,10 +2539,10 @@ sx_assert(&allprison_lock, SX_LOCKED); FOREACH_PRISON_DESCENDANT(mypr, pr, descend) { if (pr->pr_id == prid) { - mtx_lock(&pr->pr_mtx); - if (prison_isvalid(pr)) + if (prison_isvalid(pr)) { + mtx_lock(&pr->pr_mtx); return (pr); - mtx_unlock(&pr->pr_mtx); + } } } return (NULL); @@ -2580,26 +2560,19 @@ sx_assert(&allprison_lock, SX_LOCKED); mylen = (mypr == &prison0) ? 0 : strlen(mypr->pr_name) + 1; - again: deadpr = NULL; FOREACH_PRISON_DESCENDANT(mypr, pr, descend) { - if (!strcmp(pr->pr_name + mylen, name)) { - mtx_lock(&pr->pr_mtx); - if (prison_isalive(pr)) + if (prison_isvalid(pr) && !strcmp(pr->pr_name + mylen, name)) { + if (prison_isalive(pr)) { + mtx_lock(&pr->pr_mtx); return (pr); - if (prison_isvalid(pr)) - deadpr = pr; - mtx_unlock(&pr->pr_mtx); + } + deadpr = pr; } } /* There was no valid prison - perhaps there was a dying one. */ - if (deadpr != NULL) { + if (deadpr != NULL) mtx_lock(&deadpr->pr_mtx); - if (!prison_isvalid(deadpr)) { - mtx_unlock(&deadpr->pr_mtx); - goto again; - } - } return (deadpr); } @@ -2653,45 +2626,53 @@ /* * Remove a prison reference. If that was the last reference, the - * prison will be removed (at a later time). Return with the prison - * unlocked. + * prison will be removed (at a later time). */ void prison_free_locked(struct prison *pr) { - int lastref; mtx_assert(&pr->pr_mtx, MA_OWNED); + /* + * Locking is no longer required, but unlock because the caller + * expects it. + */ + mtx_unlock(&pr->pr_mtx); + prison_free(pr); +} + +void +prison_free(struct prison *pr) +{ + KASSERT(refcount_load(&pr->pr_ref) > 0, ("Trying to free dead prison %p (jid=%d).", pr, pr->pr_id)); - lastref = refcount_release(&pr->pr_ref); - mtx_unlock(&pr->pr_mtx); - if (lastref) { + if (!refcount_release_if_not_last(&pr->pr_ref)) { /* - * Don't remove the prison itself in this context, + * Don't remove the last reference in this context, * in case there are locks held. */ taskqueue_enqueue(taskqueue_thread, &pr->pr_task); } } -void -prison_free(struct prison *pr) +static void +prison_free_not_last(struct prison *pr) { +#ifdef INVARIANTS + int lastref; - /* - * Locking is only required when releasing the last reference. - * This allows assurance that a locked prison will remain valid - * until it is unlocked. - */ KASSERT(refcount_load(&pr->pr_ref) > 0, ("Trying to free dead prison %p (jid=%d).", pr, pr->pr_id)); - if (refcount_release_if_not_last(&pr->pr_ref)) - return; - mtx_lock(&pr->pr_mtx); - prison_free_locked(pr); + lastref = refcount_release(&pr->pr_ref); + KASSERT(!lastref, + ("prison_free_not_last freed last ref on prison %p (jid=%d).", + pr, pr->pr_id)); +#else + refcount_release(&pr>pr_ref); +#endif } /* @@ -2738,7 +2719,7 @@ * but also half dead. Add a reference so any calls to * prison_free() won't re-submit the task. */ - refcount_acquire(&pr->pr_ref); + prison_hold(pr); taskqueue_enqueue(taskqueue_thread, &pr->pr_task); } } @@ -2750,18 +2731,18 @@ prison_complete(void *context, int pending) { struct prison *pr = context; + int drflags; - sx_xlock(&allprison_lock); - mtx_lock(&pr->pr_mtx); /* - * If this is completing a call to prison_proc_free, there will still - * be a user reference held; clear that as well as the reference that - * was added. No references are expected if this is completing a call - * to prison_free, but prison_deref is still called for the cleanup. + * This could be called to release the last reference, or the + * last user reference; the existence of a user reference implies + * the latter. There will always be a reference to remove, as + * prison_proc_free adds one. */ - prison_deref(pr, refcount_load(&pr->pr_uref) > 0 - ? PD_DEREF | PD_DEUREF | PD_LOCKED | PD_LIST_XLOCKED - : PD_LOCKED | PD_LIST_XLOCKED); + drflags = prison_lock_xlock(pr, PD_DEREF); + if (refcount_load(&pr->pr_uref) > 0) + drflags |= PD_DEUREF; + prison_deref(pr, drflags); } /* @@ -2776,69 +2757,61 @@ prison_deref(struct prison *pr, int flags) { struct prison *ppr, *tpr; - int lastref, lasturef; - if (!(flags & PD_LOCKED)) - mtx_lock(&pr->pr_mtx); for (;;) { if (flags & PD_DEUREF) { + /* Drop a user reference. */ KASSERT(refcount_load(&pr->pr_uref) > 0, ("prison_deref PD_DEUREF on a dead prison (jid=%d)", pr->pr_id)); - lasturef = refcount_release(&pr->pr_uref); - if (lasturef) - refcount_acquire(&pr->pr_ref); - KASSERT(refcount_load(&prison0.pr_uref) > 0, - ("prison0 pr_uref=0")); - } else - lasturef = 0; + if (!refcount_release_if_not_last(&pr->pr_uref)) { + /* This is the last user reference. */ + prison_hold(pr); + flags = prison_lock_xlock(pr, flags); + if (refcount_release(&pr->pr_uref)) { + /* + * Tell the modules if the last user + * reference was removed (even if the + * prison sticks around in dying state). + */ + KASSERT( + refcount_load(&prison0.pr_uref) > 0, + ("prison0 pr_uref=0")); + mtx_unlock(&pr->pr_mtx); + flags &= ~PD_LOCKED; + (void)osd_jail_call(pr, + PR_METHOD_REMOVE, NULL); + } + if (!(flags & PD_DEREF)) + flags |= PD_DEREF; + else + prison_free_not_last(pr); + } + } if (flags & PD_DEREF) { + /* Drop a reference. */ KASSERT(refcount_load(&pr->pr_ref) > 0, ("prison_deref PD_DEREF on a dead prison (jid=%d)", pr->pr_id)); - lastref = refcount_release(&pr->pr_ref); - } - else - lastref = refcount_load(&pr->pr_ref) == 0; - mtx_unlock(&pr->pr_mtx); - - /* - * Tell the modules if the last user reference was removed - * (even it sticks around in dying state). - */ - if (lasturef) { - if (!(flags & (PD_LIST_SLOCKED | PD_LIST_XLOCKED))) { - if (atomic_load_acq_int(&pr->pr_ref) > 1) { - sx_slock(&allprison_lock); - flags |= PD_LIST_SLOCKED; - } else { - sx_xlock(&allprison_lock); - flags |= PD_LIST_XLOCKED; - } + if (refcount_release_if_not_last(&pr->pr_ref)) + break; + /* This is the last reference. */ + flags = prison_lock_xlock(pr, flags); + if (!refcount_release(&pr->pr_ref)) + break; + } else { + /* Check the existing reference count. */ + if (!(flags & + (PD_LOCKED | PD_LIST_SLOCKED | PD_LIST_XLOCKED))) { + mtx_lock(&pr->pr_mtx); + flags |= PD_LOCKED; } - (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL); - mtx_lock(&pr->pr_mtx); - lastref = refcount_release(&pr->pr_ref); - mtx_unlock(&pr->pr_mtx); + if (refcount_load(&pr->pr_ref) > 0) + break; + flags = prison_lock_xlock(pr, flags); } - /* If the prison still has references, nothing else to do. */ - if (!lastref) { - if (flags & PD_LIST_SLOCKED) - sx_sunlock(&allprison_lock); - else if (flags & PD_LIST_XLOCKED) - sx_xunlock(&allprison_lock); - return; - } - - if (flags & PD_LIST_SLOCKED) { - if (!sx_try_upgrade(&allprison_lock)) { - sx_sunlock(&allprison_lock); - sx_xlock(&allprison_lock); - } - } else if (!(flags & PD_LIST_XLOCKED)) - sx_xlock(&allprison_lock); - + /* Do the work of actually freeing the prison. */ TAILQ_REMOVE(&allprison, pr, pr_list); LIST_REMOVE(pr, pr_sibling); ppr = pr->pr_parent; @@ -2870,12 +2843,55 @@ /* Removing a prison frees a reference on its parent. */ pr = ppr; - mtx_lock(&pr->pr_mtx); flags = PD_DEREF | PD_DEUREF; } + + /* Nothing more to do: release locks and return. */ + if (flags & PD_LOCKED) + mtx_unlock(&pr->pr_mtx); + if (flags & PD_LIST_SLOCKED) + sx_sunlock(&allprison_lock); + else if (flags & PD_LIST_XLOCKED) + sx_xunlock(&allprison_lock); } /* + * Given the current locking state in the flags, make sure allprison_lock + * is held exclusive, and the prison is locked. Return flags indicating + * the new state. + */ +static int +prison_lock_xlock(struct prison *pr, int flags) +{ + + if (!(flags & PD_LIST_XLOCKED)) { + /* + * Get allprison_lock, which may be an upgrade, + * and may require unlocking the prison. + */ + if (flags & PD_LOCKED) { + mtx_unlock(&pr->pr_mtx); + flags &= ~PD_LOCKED; + } + if (flags & PD_LIST_SLOCKED) { + if (!sx_try_upgrade(&allprison_lock)) { + sx_sunlock(&allprison_lock); + sx_xlock(&allprison_lock); + } + flags &= ~PD_LIST_SLOCKED; + } else + sx_xlock(&allprison_lock); + flags |= PD_LIST_XLOCKED; + } + if (!(flags & PD_LOCKED)) { + /* Lock the prison mutex. */ + mtx_lock(&pr->pr_mtx); + flags |= PD_LOCKED; + } + return flags; +} + +/* * Set or clear a permission bit in the pr_allow field, passing restrictions * (cleared permission) down to child jails. */ @@ -3037,31 +3053,26 @@ } /* - * Return true if the prison is currently alive. A prison is alive if it is - * valid and it holds user references. + * Return true if the prison is currently alive. A prison is alive if it + * holds user references. */ bool prison_isalive(struct prison *pr) { - mtx_assert(&pr->pr_mtx, MA_OWNED); - if (__predict_false(refcount_load(&pr->pr_ref) == 0)) - return (false); if (__predict_false(refcount_load(&pr->pr_uref) == 0)) return (false); return (true); } /* - * Return true if the prison is currently valid. A prison is valid if it has - * been fully created, and is not being destroyed. Note that dying prisons - * are still considered valid. + * Return true if the prison is currently valid, i.e. it has been fully + * created. Note that dying prisons are still considered valid. */ bool prison_isvalid(struct prison *pr) { - mtx_assert(&pr->pr_mtx, MA_OWNED); if (__predict_false(refcount_load(&pr->pr_ref) == 0)) return (false); return (true); @@ -3696,6 +3707,8 @@ #if defined(INET) || defined(INET6) again: #endif + if (!prison_isvalid(cpr)) + continue; mtx_lock(&cpr->pr_mtx); #ifdef INET if (cpr->pr_ip4s > 0) { @@ -3723,10 +3736,6 @@ cpr->pr_ip6s * sizeof(struct in6_addr)); } #endif - if (!prison_isvalid(cpr)) { - mtx_unlock(&cpr->pr_mtx); - continue; - } bzero(xp, sizeof(*xp)); xp->pr_version = XPRISON_VERSION; xp->pr_id = cpr->pr_id; diff --git a/kern/sysv_msg.c b/kern/sysv_msg_28419.c --- a/kern/sysv_msg.c +++ b/kern/sysv_msg_28419.c @@ -287,10 +287,12 @@ rsv = NULL; sx_slock(&allprison_lock); TAILQ_FOREACH(pr, &allprison, pr_list) { + if (!prison_isvalid(pr)) + continue; if (rsv == NULL) rsv = osd_reserve(msg_prison_slot); prison_lock(pr); - if (prison_isvalid(pr) && (pr->pr_allow & PR_ALLOW_SYSVIPC)) { + if (pr->pr_allow & PR_ALLOW_SYSVIPC) { (void)osd_jail_set_reserved(pr, msg_prison_slot, rsv, &prison0); rsv = NULL; diff --git a/kern/sysv_sem.c b/kern/sysv_sem_28419.c --- a/kern/sysv_sem.c +++ b/kern/sysv_sem_28419.c @@ -318,10 +318,12 @@ rsv = NULL; sx_slock(&allprison_lock); TAILQ_FOREACH(pr, &allprison, pr_list) { + if (!prison_isvalid(pr)) + continue; if (rsv == NULL) rsv = osd_reserve(sem_prison_slot); prison_lock(pr); - if (prison_isvalid(pr) && (pr->pr_allow & PR_ALLOW_SYSVIPC)) { + if (pr->pr_allow & PR_ALLOW_SYSVIPC) { (void)osd_jail_set_reserved(pr, sem_prison_slot, rsv, &prison0); rsv = NULL; diff --git a/kern/sysv_shm.c b/kern/sysv_shm_28419.c --- a/kern/sysv_shm.c +++ b/kern/sysv_shm_28419.c @@ -976,10 +976,12 @@ rsv = NULL; sx_slock(&allprison_lock); TAILQ_FOREACH(pr, &allprison, pr_list) { + if (!prison_isvalid(pr)) + continue; if (rsv == NULL) rsv = osd_reserve(shm_prison_slot); prison_lock(pr); - if (prison_isvalid(pr) && (pr->pr_allow & PR_ALLOW_SYSVIPC)) { + if (pr->pr_allow & PR_ALLOW_SYSVIPC) { (void)osd_jail_set_reserved(pr, shm_prison_slot, rsv, &prison0); rsv = NULL; diff --git a/kern/uipc_mqueue.c b/kern/uipc_mqueue_28419.c --- a/kern/uipc_mqueue.c +++ b/kern/uipc_mqueue_28419.c @@ -1564,29 +1564,27 @@ const struct prison *pr = obj; struct prison *tpr; struct mqfs_node *pn, *tpn; - int found; + struct vnode *pr_root; - found = 0; + pr_root = pr->pr_root; + if (pr->pr_parent->pr_root == pr_root) + return (0); TAILQ_FOREACH(tpr, &allprison, pr_list) { - prison_lock(tpr); if (tpr != pr && prison_isvalid(tpr) && - tpr->pr_root == pr->pr_root) - found = 1; - prison_unlock(tpr); + tpr->pr_root == pr_root) + return (0); } - if (!found) { - /* - * No jails are rooted in this directory anymore, - * so no queues should be either. - */ - sx_xlock(&mqfs_data.mi_lock); - LIST_FOREACH_SAFE(pn, &mqfs_data.mi_root->mn_children, - mn_sibling, tpn) { - if (pn->mn_pr_root == pr->pr_root) - (void)do_unlink(pn, curthread->td_ucred); - } - sx_xunlock(&mqfs_data.mi_lock); + /* + * No jails are rooted in this directory anymore, + * so no queues should be either. + */ + sx_xlock(&mqfs_data.mi_lock); + LIST_FOREACH_SAFE(pn, &mqfs_data.mi_root->mn_children, + mn_sibling, tpn) { + if (pn->mn_pr_root == pr_root) + (void)do_unlink(pn, curthread->td_ucred); } + sx_xunlock(&mqfs_data.mi_lock); return (0); } diff --git a/sys/jail.h b/sys/jail_28419.h --- a/sys/jail.h +++ b/sys/jail_28419.h @@ -155,7 +155,8 @@ * (m) locked by pr_mtx * (p) locked by pr_mtx, and also at least shared allprison_lock required * to update - * (r) atomic via refcount(9), pr_mtx required to decrement to zero + * (r) atomic via refcount(9), pr_mtx and allprison_lock required to + * decrement to zero */ struct prison { TAILQ_ENTRY(prison) pr_list; /* (a) all prisons */