Page MenuHomeFreeBSD

D27876.id83135.diff
No OneTemporary

D27876.id83135.diff

diff --git a/kern/kern_jail.c b/kern/kern_jail.c
--- a/kern/kern_jail.c
+++ b/kern/kern_jail.c
@@ -106,6 +106,7 @@
.pr_path = "/",
.pr_securelevel = -1,
.pr_devfs_rsnum = 0,
+ .pr_state = PRISON_STATE_ALIVE,
.pr_childmax = JAIL_MAX,
.pr_hostuuid = DEFAULT_HOSTUUID,
.pr_children = LIST_HEAD_INITIALIZER(prison0.pr_children),
@@ -1040,7 +1041,8 @@
error = ENOENT;
vfs_opterror(opts, "jail %d not found", jid);
goto done_deref;
- } else if (!prison_isalive(pr)) {
+ }
+ if (!prison_isalive(pr)) {
if (!(flags & JAIL_DYING)) {
error = ENOENT;
vfs_opterror(opts, "jail %d is dying",
@@ -1200,6 +1202,8 @@
/* This brings the parent back to life. */
mtx_lock(&ppr->pr_mtx);
refcount_acquire(&ppr->pr_uref);
+ if (ppr->pr_state == PRISON_STATE_DYING)
+ ppr->pr_state = PRISON_STATE_ALIVE;
mtx_unlock(&ppr->pr_mtx);
error = osd_jail_call(ppr, PR_METHOD_CREATE, opts);
if (error) {
@@ -1218,8 +1222,10 @@
}
pr = malloc(sizeof(*pr), M_PRISON, M_WAITOK | M_ZERO);
- refcount_init(&pr->pr_ref, 0);
+ pr->pr_state = PRISON_STATE_INVALID;
+ refcount_init(&pr->pr_ref, 1);
refcount_init(&pr->pr_uref, 0);
+ drflags |= PD_DEREF;
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);
@@ -1313,11 +1319,6 @@
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 {
/*
* Grab a reference for existing prisons, to ensure they
@@ -1739,15 +1740,15 @@
prison_set_allow_locked(pr, tallow, 0);
/*
* Persistent prisons get an extra reference, and prisons losing their
- * persist flag lose that reference. Only do this for existing prisons
- * for now, so new ones will remain unseen until after the module
- * handlers have completed.
+ * persist flag lose that reference.
*/
born = !prison_isalive(pr);
- if (!created && (ch_flags & PR_PERSIST & (pr_flags ^ pr->pr_flags))) {
+ if (ch_flags & PR_PERSIST & (pr_flags ^ pr->pr_flags)) {
if (pr_flags & PR_PERSIST) {
prison_hold(pr);
- refcount_acquire(&pr->pr_uref);
+ if (refcount_acquire(&pr->pr_uref) == 0 &&
+ pr->pr_state == PRISON_STATE_DYING)
+ pr->pr_state = PRISON_STATE_ALIVE;
} else {
drflags |= PD_DEUREF;
prison_free_not_last(pr);
@@ -1817,15 +1818,13 @@
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);
- goto done_deref;
- }
+ if (error)
+ goto done_remove;
}
error = osd_jail_call(pr, PR_METHOD_SET, opts);
if (error) {
if (born)
- (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL);
+ goto done_remove;
goto done_deref;
}
@@ -1835,11 +1834,9 @@
error = do_jail_attach(td, pr);
drflags &= ~PD_LIST_SLOCKED;
if (error) {
- if (created) {
- /* do_jail_attach has removed the prison. */
- pr = NULL;
- }
vfs_opterror(opts, "attach failed");
+ if (born)
+ goto done_remove;
goto done_deref;
}
}
@@ -1854,23 +1851,34 @@
}
#endif
+ if (created) {
+ /* The new prison is now ready to be seen. */
+ drflags = prison_lock_xlock(pr, drflags);
+ pr->pr_state = refcount_load(&pr->pr_uref) > 0
+ ? PRISON_STATE_ALIVE : PRISON_STATE_DYING;
+ }
+
td->td_retval[0] = pr->pr_id;
+ goto done_deref;
- 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) {
- drflags = prison_lock_xlock(pr, drflags);
- refcount_acquire(&pr->pr_ref);
- refcount_acquire(&pr->pr_uref);
- } else {
- /* Non-persistent jails need no further changes. */
- pr = NULL;
- }
+ done_remove:
+ /*
+ * prison_deref will call the remove methods when alive prisons die;
+ * otherwise it needs to be called now.
+ */
+ if (!(drflags & (PD_LIST_SLOCKED | PD_LIST_XLOCKED))) {
+ sx_xlock(&allprison_lock);
+ drflags |= PD_LIST_XLOCKED;
}
+ if (pr->pr_state != PRISON_STATE_ALIVE)
+ (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL);
+ /* Remove the persist flag from new (or resurrected) prisons. */
+ drflags = prison_lock_xlock(pr, drflags);
+ if (pr->pr_flags & PR_PERSIST) {
+ pr->pr_flags &= ~PR_PERSIST;
+ drflags |= PD_DEUREF;
+ prison_free_not_last(pr);
+ }
done_deref:
/* Release any temporary prison holds and/or locks. */
@@ -2437,8 +2445,10 @@
*/
drflags = prison_lock_xlock(pr, drflags);
}
- refcount_acquire(&pr->pr_ref);
- refcount_acquire(&pr->pr_uref);
+ prison_hold(pr);
+ if (refcount_acquire(&pr->pr_uref) == 0 &&
+ pr->pr_state == PRISON_STATE_DYING)
+ pr->pr_state = PRISON_STATE_ALIVE;
mtx_unlock(&pr->pr_mtx);
drflags &= ~PD_LOCKED;
@@ -2720,6 +2730,12 @@
* prison_free() won't re-submit the task.
*/
prison_hold(pr);
+ mtx_lock(&pr->pr_mtx);
+ KASSERT(!(pr->pr_flags & PR_COMPLETE_PROC),
+ ("Redundant last reference in prison_proc_free (jid=%d)",
+ pr->pr_id));
+ pr->pr_flags |= PR_COMPLETE_PROC;
+ mtx_unlock(&pr->pr_mtx);
taskqueue_enqueue(taskqueue_thread, &pr->pr_task);
}
}
@@ -2740,8 +2756,10 @@
* prison_proc_free adds one.
*/
drflags = prison_lock_xlock(pr, PD_DEREF);
- if (refcount_load(&pr->pr_uref) > 0)
+ if (pr->pr_flags & PR_COMPLETE_PROC) {
+ pr->pr_flags &= ~PR_COMPLETE_PROC;
drflags |= PD_DEUREF;
+ }
prison_deref(pr, drflags);
}
@@ -2768,20 +2786,21 @@
/* This is the last user reference. */
prison_hold(pr);
flags = prison_lock_xlock(pr, flags);
- if (refcount_release(&pr->pr_uref)) {
+ if (refcount_release(&pr->pr_uref) &&
+ pr->pr_state == PRISON_STATE_ALIVE) {
/*
* 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"));
+ pr->pr_state = PRISON_STATE_DYING;
mtx_unlock(&pr->pr_mtx);
flags &= ~PD_LOCKED;
(void)osd_jail_call(pr,
PR_METHOD_REMOVE, NULL);
}
+ KASSERT(refcount_load(&prison0.pr_uref) > 0,
+ ("prison0 pr_uref=0"));
if (!(flags & PD_DEREF))
flags |= PD_DEREF;
else
@@ -2799,19 +2818,11 @@
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;
- }
- if (refcount_load(&pr->pr_ref) > 0)
- break;
- flags = prison_lock_xlock(pr, flags);
- }
+ } else
+ break;
/* Do the work of actually freeing the prison. */
+ pr->pr_state = PRISON_STATE_INVALID;
TAILQ_REMOVE(&allprison, pr, pr_list);
LIST_REMOVE(pr, pr_sibling);
ppr = pr->pr_parent;
@@ -3060,7 +3071,7 @@
prison_isalive(struct prison *pr)
{
- if (__predict_false(refcount_load(&pr->pr_uref) == 0))
+ if (__predict_false(pr->pr_state != PRISON_STATE_ALIVE))
return (false);
return (true);
}
@@ -3073,7 +3084,7 @@
prison_isvalid(struct prison *pr)
{
- if (__predict_false(refcount_load(&pr->pr_ref) == 0))
+ if (__predict_false(pr->pr_state == PRISON_STATE_INVALID))
return (false);
return (true);
}
@@ -3739,8 +3750,7 @@
bzero(xp, sizeof(*xp));
xp->pr_version = XPRISON_VERSION;
xp->pr_id = cpr->pr_id;
- xp->pr_state = prison_isalive(cpr)
- ? PRISON_STATE_ALIVE : PRISON_STATE_DYING;
+ xp->pr_state = cpr->pr_state;
strlcpy(xp->pr_path, prison_path(pr, cpr), sizeof(xp->pr_path));
strlcpy(xp->pr_host, cpr->pr_hostname, sizeof(xp->pr_host));
strlcpy(xp->pr_name, prison_name(pr, cpr), sizeof(xp->pr_name));
@@ -4391,6 +4401,7 @@
db_printf(" parent = %p\n", pr->pr_parent);
db_printf(" ref = %d\n", pr->pr_ref);
db_printf(" uref = %d\n", pr->pr_uref);
+ db_printf(" state = %d\n", pr->pr_state);
db_printf(" path = %s\n", pr->pr_path);
db_printf(" cpuset = %d\n", pr->pr_cpuset
? pr->pr_cpuset->cs_id : -1);
diff --git a/sys/jail.h b/sys/jail.h
--- a/sys/jail.h
+++ b/sys/jail.h
@@ -88,9 +88,11 @@
};
#define XPRISON_VERSION 3
-#define PRISON_STATE_INVALID 0
-#define PRISON_STATE_ALIVE 1
-#define PRISON_STATE_DYING 2
+enum prison_state {
+ PRISON_STATE_INVALID = 0, /* New prison, not ready to be seen */
+ PRISON_STATE_ALIVE, /* Current prison, visible to all */
+ PRISON_STATE_DYING /* Removed but holding resources, */
+}; /* optionally visible. */
/*
* Flags for jail_set and jail_get.
@@ -155,6 +157,7 @@
* (m) locked by pr_mtx
* (p) locked by pr_mtx, and also at least shared allprison_lock required
* to update
+ * (q) locked by both pr_mtx and allprison_lock
* (r) atomic via refcount(9), pr_mtx and allprison_lock required to
* decrement to zero
*/
@@ -185,7 +188,8 @@
int pr_securelevel; /* (p) securelevel */
int pr_enforce_statfs; /* (p) statfs permission */
int pr_devfs_rsnum; /* (p) devfs ruleset */
- int pr_spare[3];
+ enum prison_state pr_state; /* (q) state in life cycle */
+ int pr_spare[2];
int pr_osreldate; /* (c) kern.osreldate value */
unsigned long pr_hostid; /* (p) jail hostid */
char pr_name[MAXHOSTNAMELEN]; /* (p) admin jail name */
@@ -221,6 +225,8 @@
/* by this jail or an ancestor */
#define PR_IP6 0x04000000 /* IPv6 restricted or disabled */
/* by this jail or an ancestor */
+#define PR_COMPLETE_PROC 0x08000000 /* prison_complete called from */
+ /* prison_proc_free, releases uref */
/*
* Flags for pr_allow

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 16, 10:45 AM (20 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29767190
Default Alt Text
D27876.id83135.diff (9 KB)

Event Timeline