Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142732491
D28150.id84345.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D28150.id84345.diff
View Options
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c
--- a/sys/kern/kern_jail.c
+++ b/sys/kern/kern_jail.c
@@ -136,8 +136,10 @@
struct prisonlist allprison = TAILQ_HEAD_INITIALIZER(allprison);
LIST_HEAD(, prison_racct) allprison_racct;
int lastprid = 0;
+int lastdeadid = 0;
static int get_next_prid(struct prison **insprp);
+static int get_next_deadid(struct prison **dinsprp);
static int do_jail_attach(struct thread *td, struct prison *pr, int drflags);
static void prison_complete(void *context, int pending);
static void prison_deref(struct prison *pr, int flags);
@@ -518,7 +520,7 @@
#endif
struct vfsopt *opt;
struct vfsoptlist *opts;
- struct prison *pr, *deadpr, *inspr, *mypr, *ppr, *tpr;
+ struct prison *pr, *deadpr, *dinspr, *inspr, *mypr, *ppr, *tpr;
struct vnode *root;
char *domain, *errmsg, *host, *name, *namelc, *p, *path, *uuid;
char *g_path, *osrelstr;
@@ -530,10 +532,10 @@
#endif
unsigned long hid;
size_t namelen, onamelen, pnamelen;
- int born, created, cuflags, descend, drflags, enforce;
+ int created, cuflags, descend, drflags, enforce;
int error, errmsg_len, errmsg_pos;
int gotchildmax, gotenforce, gothid, gotrsnum, gotslevel;
- int jid, jsys, len, level;
+ int deadid, jid, jsys, len, level;
int childmax, osreldt, rsnum, slevel;
#if defined(INET) || defined(INET6)
int ii, ij;
@@ -991,6 +993,7 @@
pr = NULL;
ppr = mypr;
inspr = NULL;
+ deadpr = NULL;
if (cuflags == JAIL_CREATE && jid == 0 && name != NULL) {
namelc = strrchr(name, '.');
jid = strtoul(namelc != NULL ? namelc + 1 : name, &p, 10);
@@ -1019,66 +1022,41 @@
continue;
if (inspr->pr_id > jid)
break;
- pr = inspr;
- KASSERT(prison_isvalid(pr),
+ KASSERT(prison_isvalid(inspr),
("Found invalid prison %p", pr));
- mtx_lock(&pr->pr_mtx);
- drflags |= PD_LOCKED;
+ if (prison_isalive(inspr)) {
+ pr = inspr;
+ mtx_lock(&pr->pr_mtx);
+ drflags |= PD_LOCKED;
+ } else {
+ /* Note a dying jail to handle later. */
+ deadpr = inspr;
+ }
inspr = NULL;
break;
}
- if (pr != NULL) {
- ppr = pr->pr_parent;
- /* Create: jid must not exist. */
- if (cuflags == JAIL_CREATE) {
- /*
- * Even creators that cannot see the jail will
- * get EEXIST.
- */
- error = EEXIST;
- vfs_opterror(opts, "jail %d already exists",
- jid);
- goto done_deref;
- }
- if (!prison_ischild(mypr, pr)) {
- /*
- * Updaters get ENOENT if they cannot see the
- * jail. This is true even for CREATE | UPDATE,
- * which normally cannot give this error.
- */
- error = ENOENT;
- vfs_opterror(opts, "jail %d not found", jid);
- goto done_deref;
- }
- if (!prison_isalive(pr)) {
- if (!(flags & JAIL_DYING)) {
- error = ENOENT;
- vfs_opterror(opts, "jail %d is dying",
- jid);
- goto done_deref;
- }
- if ((flags & JAIL_ATTACH) ||
- (pr_flags & PR_PERSIST)) {
- /*
- * A dying jail might be resurrected
- * (via attach or persist), but first
- * it must determine if another jail
- * has claimed its name. Accomplish
- * this by implicitly re-setting the
- * name.
- */
- if (name == NULL)
- name = prison_name(mypr, pr);
- }
- }
- } else {
- /* Update: jid must exist. */
- if (cuflags == JAIL_UPDATE) {
- error = ENOENT;
- vfs_opterror(opts, "jail %d not found", jid);
- goto done_deref;
- }
+ if (cuflags == JAIL_CREATE && pr != NULL) {
+ /*
+ * Even creators that cannot see the jail will get
+ * EEXIST.
+ */
+ error = EEXIST;
+ vfs_opterror(opts, "jail %d already exists", jid);
+ goto done_deref;
}
+ if ((pr == NULL)
+ ? cuflags == JAIL_UPDATE
+ : !prison_ischild(mypr, pr)) {
+ /*
+ * Updaters get ENOENT for nonexistent jails, or for
+ * jails they cannot see. The latter case is true
+ * even for CREATE | UPDATE, which normally cannot
+ * give this error.
+ */
+ error = ENOENT;
+ vfs_opterror(opts, "jail %d not found", jid);
+ goto done_deref;
+ }
}
/*
* If the caller provided a name, look for a jail by that name.
@@ -1115,14 +1093,13 @@
"jail \"%s\" not found", name);
goto done_deref;
}
+ mtx_unlock(&ppr->pr_mtx);
if (!prison_isalive(ppr)) {
- mtx_unlock(&ppr->pr_mtx);
error = ENOENT;
vfs_opterror(opts,
"jail \"%s\" is dying", name);
goto done_deref;
}
- mtx_unlock(&ppr->pr_mtx);
*namelc = '.';
}
namelc++;
@@ -1130,57 +1107,35 @@
if (namelc[0] != '\0') {
pnamelen =
(ppr == &prison0) ? 0 : strlen(ppr->pr_name) + 1;
- deadpr = NULL;
FOREACH_PRISON_CHILD(ppr, tpr) {
- if (tpr != pr &&
- !strcmp(tpr->pr_name + pnamelen, namelc)) {
- if (prison_isalive(tpr)) {
- if (pr == NULL &&
- cuflags != JAIL_CREATE) {
- /*
- * Use this jail
- * for updates.
- */
- pr = tpr;
- mtx_lock(&tpr->pr_mtx);
- drflags |= PD_LOCKED;
- break;
- }
- /*
- * Create, or update(jid):
- * name must not exist in an
- * active sibling jail.
- */
- error = EEXIST;
- vfs_opterror(opts,
- "jail \"%s\" already exists",
- name);
- goto done_deref;
- }
- if (pr == NULL &&
- cuflags != JAIL_CREATE) {
- deadpr = tpr;
- KASSERT(prison_isvalid(deadpr),
- ("Found invalid prison %p",
- deadpr));
- }
- }
- }
- /* If no active jail is found, use a dying one. */
- if (deadpr != NULL && pr == NULL) {
- if (flags & JAIL_DYING) {
- pr = deadpr;
- mtx_lock(&pr->pr_mtx);
- drflags |= PD_LOCKED;
- } else if (cuflags == JAIL_UPDATE) {
- error = ENOENT;
+ if (tpr == pr || !prison_isalive(tpr) ||
+ strcmp(tpr->pr_name + pnamelen, namelc))
+ continue;
+ KASSERT(prison_isvalid(tpr),
+ ("Found invalid prison %p", tpr));
+ if (cuflags == JAIL_CREATE || pr != NULL) {
+ /*
+ * Create, or update(jid): name must
+ * not exist in an active sibling jail.
+ */
+ error = EEXIST;
vfs_opterror(opts,
- "jail \"%s\" is dying", name);
+ "jail \"%s\" already exists", name);
goto done_deref;
}
+ /* Use this jail for updates. */
+ pr = tpr;
+ mtx_lock(&pr->pr_mtx);
+ drflags |= PD_LOCKED;
+ break;
}
- /* Update: name must exist if no jid. */
- else if (cuflags == JAIL_UPDATE && pr == NULL) {
+ /*
+ * Update: name must exist if no jid. As with the jid
+ * case, the jail must be currently visible, or else
+ * even CREATE | UPDATE will get an error.
+ */
+ if ((pr == NULL)
+ ? cuflags == JAIL_UPDATE : !prison_isalive(pr)) {
error = ENOENT;
vfs_opterror(opts, "jail \"%s\" not found",
name);
@@ -1204,14 +1159,40 @@
vfs_opterror(opts, "prison limit exceeded");
goto done_deref;
}
- prison_hold(ppr);
- prison_proc_hold(ppr);
+ if (deadpr != NULL) {
+ /*
+ * The prison being created has the same ID as a dying
+ * one. Handle this by giving the dying jail a new ID.
+ * This may cause some confusion to user space, but
+ * only to those listing dying jails.
+ */
+ deadid = get_next_deadid(&dinspr);
+ if (deadid == 0) {
+ error = EAGAIN;
+ vfs_opterror(opts, "no available jail IDs");
+ goto done_deref;
+ }
+ mtx_lock(&deadpr->pr_mtx);
+ deadpr->pr_id = deadid;
+ mtx_unlock(&deadpr->pr_mtx);
+ if (dinspr == deadpr)
+ inspr = deadpr;
+ else {
+ inspr = TAILQ_NEXT(deadpr, pr_list);
+ TAILQ_REMOVE(&allprison, deadpr, pr_list);
+ if (dinspr != NULL)
+ TAILQ_INSERT_AFTER(&allprison, dinspr,
+ deadpr, pr_list);
+ else
+ TAILQ_INSERT_HEAD(&allprison, deadpr,
+ pr_list);
+ }
+ }
+
if (jid == 0 && (jid = get_next_prid(&inspr)) == 0) {
error = EAGAIN;
vfs_opterror(opts, "no available jail IDs");
- pr = ppr;
- drflags |= PD_DEREF | PD_DEUREF;
goto done_deref;
}
@@ -1231,6 +1212,8 @@
TAILQ_INSERT_TAIL(&allprison, pr, pr_list);
pr->pr_parent = ppr;
+ prison_hold(ppr);
+ prison_proc_hold(ppr);
LIST_INSERT_HEAD(&ppr->pr_children, pr, pr_sibling);
for (tpr = ppr; tpr != NULL; tpr = tpr->pr_parent)
tpr->pr_childcount++;
@@ -1736,13 +1719,10 @@
* Persistent prisons get an extra reference, and prisons losing their
* persist flag lose that reference.
*/
- born = !prison_isalive(pr);
if (ch_flags & PR_PERSIST & (pr_flags ^ pr->pr_flags)) {
if (pr_flags & PR_PERSIST) {
prison_hold(pr);
- if (refcount_acquire(&pr->pr_uref) == 0 &&
- pr->pr_state == PRISON_STATE_DYING)
- pr->pr_state = PRISON_STATE_ALIVE;
+ refcount_acquire(&pr->pr_uref);
} else {
drflags |= PD_DEUREF;
prison_free_not_last(pr);
@@ -1814,7 +1794,7 @@
#endif
/* Let the modules do their work. */
- if (born) {
+ if (created) {
error = osd_jail_call(pr, PR_METHOD_CREATE, opts);
if (error)
goto done_deref;
@@ -1951,6 +1931,55 @@
*insprp = inspr;
lastprid = jid;
return (jid);
+}
+
+/*
+ * Find the next available ID for a renumbered dead prison. This is the same
+ * as get_next_prid, but counting backward from the end of the range.
+ */
+static int
+get_next_deadid(struct prison **dinsprp)
+{
+ struct prison *dinspr;
+ int deadid, minid;
+
+ deadid = lastdeadid ? lastdeadid - 1 : JAIL_MAX;
+ /*
+ * Take two reverse passes through the allprison list: first starting
+ * with the proposed deadid, then ending with it.
+ */
+ for (minid = 1; minid != 0; ) {
+ TAILQ_FOREACH_REVERSE(dinspr, &allprison, prisonlist, pr_list) {
+ if (dinspr->pr_id > deadid)
+ continue;
+ if (dinspr->pr_id < deadid) {
+ /* Found an opening. */
+ minid = 0;
+ break;
+ }
+ if (--deadid < minid) {
+ if (lastdeadid == minid || lastdeadid == 0)
+ {
+ /*
+ * The entire legal range
+ * has been traversed
+ */
+ return 0;
+ }
+ /* Try again from the start. */
+ deadid = JAIL_MAX;
+ minid = lastdeadid;
+ break;
+ }
+ }
+ if (dinspr == NULL) {
+ /* Found room at the end of the list. */
+ break;
+ }
+ }
+ *dinsprp = dinspr;
+ lastdeadid = deadid;
+ return (deadid);
}
/*
diff --git a/sys/sys/jail.h b/sys/sys/jail.h
--- a/sys/sys/jail.h
+++ b/sys/sys/jail.h
@@ -101,7 +101,7 @@
#define JAIL_UPDATE 0x02 /* Update parameters of existing jail */
#define JAIL_ATTACH 0x04 /* Attach to jail upon creation */
#define JAIL_DYING 0x08 /* Allow getting a dying jail */
-#define JAIL_SET_MASK 0x0f
+#define JAIL_SET_MASK 0x0f /* JAIL_DYING is deprecated/ignored here */
#define JAIL_GET_MASK 0x08
#define JAIL_SYS_DISABLE 0
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 23, 8:27 PM (10 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27889363
Default Alt Text
D28150.id84345.diff (10 KB)
Attached To
Mode
D28150: jail: Don't allow resurrection of dead jails
Attached
Detach File
Event Timeline
Log In to Comment