jail: Don't allow jails under dying parents

If a jail is created with jail_set(...JAIL_DYING), and it has a parent
currently in a dying state, that will bring the parent jail back to
life.  Restrict that to require that the parent itself be explicitly
brought back first, and not implicitly created along with the new
child jail.

Differential Revision:	https://reviews.freebsd.org/D28515
This commit is contained in:
Jamie Gritton 2021-02-22 17:04:06 -08:00
parent 7f06b217c5
commit 0a2a96f35a

View File

@ -990,7 +990,6 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
* This abuses the file error codes ENOENT and EEXIST.
*/
pr = NULL;
ppr = mypr;
inspr = NULL;
if (cuflags == JAIL_CREATE && jid == 0 && name != NULL) {
namelc = strrchr(name, '.');
@ -999,6 +998,12 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
jid = 0;
}
sx_xlock(&allprison_lock);
ppr = mypr;
if (!prison_isalive(ppr)) {
/* This jail is dying. This process will surely follow. */
error = EAGAIN;
goto done_deref;
}
drflags = PD_LIST_XLOCKED;
if (jid != 0) {
if (jid < 0) {
@ -1022,7 +1027,6 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
break;
}
if (pr != NULL) {
ppr = pr->pr_parent;
/* Create: jid must not exist. */
if (cuflags == JAIL_CREATE) {
/*
@ -1044,6 +1048,13 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
vfs_opterror(opts, "jail %d not found", jid);
goto done_deref;
}
ppr = pr->pr_parent;
if (!prison_isalive(ppr)) {
error = ENOENT;
vfs_opterror(opts, "jail %d is dying",
ppr->pr_id);
goto done_deref;
}
if (!prison_isalive(pr)) {
if (!(flags & JAIL_DYING)) {
error = ENOENT;
@ -1109,15 +1120,13 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
"jail \"%s\" not found", name);
goto done_deref;
}
if (!(flags & JAIL_DYING) &&
!prison_isalive(ppr)) {
mtx_unlock(&ppr->pr_mtx);
mtx_unlock(&ppr->pr_mtx);
if (!prison_isalive(ppr)) {
error = ENOENT;
vfs_opterror(opts,
"jail \"%s\" is dying", name);
goto done_deref;
}
mtx_unlock(&ppr->pr_mtx);
*namelc = '.';
}
namelc++;
@ -1196,26 +1205,9 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
vfs_opterror(opts, "prison limit exceeded");
goto done_deref;
}
prison_hold(ppr);
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);
ppr->pr_state = PRISON_STATE_ALIVE;
mtx_unlock(&ppr->pr_mtx);
error = osd_jail_call(ppr, PR_METHOD_CREATE, opts);
if (error) {
pr = ppr;
drflags |= PD_DEREF | PD_DEUREF;
goto done_deref;
}
}
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;
}
@ -1235,6 +1227,8 @@ kern_jail_set(struct thread *td, struct uio *optuio, int flags)
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++;