Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161308616
D28419.id84339.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
22 KB
Referenced Files
None
Subscribers
None
D28419.id84339.diff
View Options
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail_28419.c
--- a/sys/kern/kern_jail.c
+++ b/sys/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;
@@ -1874,8 +1864,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 {
@@ -1952,13 +1941,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;
}
@@ -2047,18 +2031,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;
@@ -2334,15 +2314,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);
@@ -2377,7 +2353,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;
}
@@ -2428,14 +2404,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);
@@ -2457,7 +2426,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
@@ -2467,17 +2436,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.
@@ -2513,7 +2492,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);
/*
@@ -2533,8 +2512,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);
}
@@ -2549,14 +2529,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)
@@ -2577,10 +2557,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);
@@ -2598,26 +2578,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);
}
@@ -2671,45 +2644,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
}
/*
@@ -2756,7 +2737,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);
}
}
@@ -2768,18 +2749,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);
}
/*
@@ -2794,69 +2775,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)) {
+ /*
+ * When the last user references goes,
+ * this becomes a dying prison (unless
+ * it was one already).
+ */
+ 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;
@@ -2888,12 +2861,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.
*/
@@ -3055,15 +3071,13 @@
}
/*
- * Return true if the prison is currently alive. A prison is alive if it is
- * valid and holds user references, and it isn't being removed.
+ * Return true if the prison is currently alive. A prison is alive if it
+ * holds user references and it isn't being removed.
*/
bool
prison_isalive(struct prison *pr)
{
- if (__predict_false(refcount_load(&pr->pr_ref) == 0))
- return (false);
if (__predict_false(refcount_load(&pr->pr_uref) == 0))
return (false);
if (__predict_false(pr->pr_flags & PR_REMOVE))
@@ -3072,9 +3086,8 @@
}
/*
- * 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)
@@ -3714,6 +3727,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) {
@@ -3741,10 +3756,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/sys/kern/sysv_msg.c b/sys/kern/sysv_msg_28419.c
--- a/sys/kern/sysv_msg.c
+++ b/sys/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/sys/kern/sysv_sem.c b/sys/kern/sysv_sem_28419.c
--- a/sys/kern/sysv_sem.c
+++ b/sys/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/sys/kern/sysv_shm.c b/sys/kern/sysv_shm_28419.c
--- a/sys/kern/sysv_shm.c
+++ b/sys/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/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue_28419.c
--- a/sys/kern/uipc_mqueue.c
+++ b/sys/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/sys/jail.h b/sys/sys/jail_28419.h
--- a/sys/sys/jail.h
+++ b/sys/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 */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jul 3, 3:51 PM (8 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34629987
Default Alt Text
D28419.id84339.diff (22 KB)
Attached To
Mode
D28419: Require allprison_lock and prison mutex when to free last prison reference
Attached
Detach File
Event Timeline
Log In to Comment