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 @@ -144,7 +144,6 @@ static void prison_deref_kill(struct prison *pr, struct prisonlist *freeprison); static int prison_lock_xlock(struct prison *pr, int flags); static void prison_free_not_last(struct prison *pr); -static void prison_proc_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); @@ -1000,6 +999,11 @@ } sx_xlock(&allprison_lock); drflags = PD_LIST_XLOCKED; + if (!prison_isalive(mypr)) { + /* This jail is dying. This process will surely follow. */ + error = EAGAIN; + goto done_deref; + } if (jid != 0) { if (jid < 0) { error = EINVAL; @@ -1111,8 +1115,7 @@ "jail \"%s\" not found", name); goto done_deref; } - if (!(flags & JAIL_DYING) && - !prison_isalive(ppr)) { + if (!prison_isalive(ppr)) { mtx_unlock(&ppr->pr_mtx); error = ENOENT; vfs_opterror(opts, @@ -1202,19 +1205,7 @@ 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; - } - } + prison_proc_hold(ppr); if (jid == 0 && (jid = get_next_prid(&inspr)) == 0) { error = EAGAIN; @@ -1758,7 +1749,6 @@ } } pr->pr_flags = (pr->pr_flags & ~ch_flags) | pr_flags; - pr->pr_flags &= ~PR_REMOVE; mtx_unlock(&pr->pr_mtx); drflags &= ~PD_LOCKED; /* @@ -2423,6 +2413,7 @@ * with it. */ if (!prison_isalive(pr)) { + /* Follow the prison into death. */ PROC_LOCK(p); kern_psignal(p, SIGKILL); PROC_UNLOCK(p); @@ -2618,7 +2609,8 @@ * user-visible, except through the the jail system calls. It is also * an error to hold an invalid prison. A prison record will remain * alive as long as it has at least one user reference, and will not - * be set to the dying state was long as the prison mutex is held. + * be set to the dying state was long as either the prison mutex or + * the allprison lock is held (allprison_lock may be shared). */ void prison_proc_hold(struct prison *pr) @@ -2667,24 +2659,6 @@ } } -static void -prison_proc_free_not_last(struct prison *pr) -{ -#ifdef INVARIANTS - int lastref; - - KASSERT(refcount_load(&pr->pr_uref) > 0, - ("Trying to free dead prison %p (jid=%d).", - pr, pr->pr_id)); - lastref = refcount_release(&pr->pr_uref); - KASSERT(!lastref, - ("prison_free_not_last freed last ref on prison %p (jid=%d).", - pr, pr->pr_id)); -#else - refcount_release(&pr>pr_uref); -#endif -} - /* * Complete a call to either prison_free or prison_proc_free. */ @@ -2732,25 +2706,13 @@ for (;;) { if (flags & PD_KILL) { /* Kill the prison and its descendents. */ - flags &= ~PD_KILL; if (!(flags & PD_DEREF)) { prison_hold(pr); flags |= PD_DEREF; } flags = prison_lock_xlock(pr, flags); - if (pr->pr_state != PRISON_STATE_DYING) { - /* - * The prison might currently be invalid, - * but call it alive now so PD_DEUREF will - * notice it later. - */ - pr->pr_state = PRISON_STATE_ALIVE; - if (!(flags & PD_DEUREF)) { - refcount_acquire(&pr->pr_uref); - flags |= PD_DEUREF; - } + if (pr->pr_state != PRISON_STATE_DYING) prison_deref_kill(pr, &freeprison); - } } if (flags & PD_DEUREF) { /* Drop a user reference. */ @@ -2902,48 +2864,56 @@ static void prison_deref_kill(struct prison *pr, struct prisonlist *freeprison) { - struct prison *cpr, *ppr; + struct prison *cpr, *ppr, *rpr; bool descend; /* - * The operation each descendant is similar to what prison_deref() - * does when losing the last references, plus clearing PR_PERSIST. + * The operation for the prison and each descendant is similar to + * what prison_deref() does when losing the last references, plus + * clearing PR_PERSIST. */ + pr->pr_state = PRISON_STATE_DYING; mtx_unlock(&pr->pr_mtx); + rpr = NULL; FOREACH_PRISON_DESCENDANT_PRE_POST(pr, cpr, descend) { - /* - * Mark the prison as doomed, so it doesn't accidentally - * come back to life. It may still be explicitly brought - * back by jail_set(2). - */ - pr->pr_flags |= PR_REMOVE; - - if (!prison_isalive(cpr)) - continue; if (descend) { + if (!prison_isalive(cpr)) { + descend = false; + continue; + } prison_hold(cpr); - prison_proc_hold(cpr); + mtx_lock(&cpr->pr_mtx); + cpr->pr_state = PRISON_STATE_DYING; + cpr->pr_flags |= PR_REMOVE; + mtx_unlock(&cpr->pr_mtx); continue; } + if (!(cpr->pr_flags & PR_REMOVE)) + continue; + if (rpr != NULL) { + LIST_REMOVE(rpr, pr_sibling); + rpr = NULL; + } + (void)osd_jail_call(cpr, PR_METHOD_REMOVE, NULL); mtx_lock(&cpr->pr_mtx); + cpr->pr_flags &= ~PR_REMOVE; if (cpr->pr_flags & PR_PERSIST) { cpr->pr_flags &= ~PR_PERSIST; - prison_proc_free_not_last(cpr); + (void)refcount_release(&cpr->pr_uref); prison_free_not_last(cpr); } - if (refcount_release(&cpr->pr_uref)) { - cpr->pr_state = PRISON_STATE_DYING; - mtx_unlock(&cpr->pr_mtx); - (void)osd_jail_call(cpr, PR_METHOD_REMOVE, NULL); - mtx_lock(&cpr->pr_mtx); - } if (refcount_release(&cpr->pr_ref)) { - cpr->pr_state = PRISON_STATE_INVALID; - TAILQ_REMOVE(&allprison, cpr, pr_list); - TAILQ_INSERT_TAIL(freeprison, cpr, pr_list); - mtx_unlock(&cpr->pr_mtx); - ppr = cpr->pr_parent; - prison_proc_free_not_last(ppr); + rpr = cpr; + rpr->pr_state = PRISON_STATE_INVALID; + mtx_unlock(&rpr->pr_mtx); + TAILQ_REMOVE(&allprison, rpr, pr_list); + TAILQ_INSERT_TAIL(freeprison, rpr, pr_list); + ppr = rpr->pr_parent; + if (!refcount_release_if_not_last(&ppr->pr_uref)) { + mtx_lock(&ppr->pr_mtx); + (void)refcount_release(&ppr->pr_uref); + mtx_unlock(&ppr->pr_mtx); + } prison_free_not_last(ppr); for (; ppr != NULL; ppr = ppr->pr_parent) ppr->pr_childcount--; @@ -2951,20 +2921,16 @@ else mtx_unlock(&cpr->pr_mtx); } + if (rpr != NULL) + LIST_REMOVE(rpr, pr_sibling); + (void)osd_jail_call(pr, PR_METHOD_REMOVE, NULL); mtx_lock(&pr->pr_mtx); if (pr->pr_flags & PR_PERSIST) { pr->pr_flags &= ~PR_PERSIST; - prison_proc_free_not_last(pr); + (void)refcount_release(&pr->pr_uref); prison_free_not_last(pr); } - - /* - * Disconnect unreferenced descendants from their parents, - * which couldn't easily be done mid-loop. - */ - TAILQ_FOREACH(cpr, freeprison, pr_list) - LIST_REMOVE(cpr, pr_sibling); } /* @@ -3173,8 +3139,6 @@ { if (__predict_false(pr->pr_state != PRISON_STATE_ALIVE)) - return (false); - if (__predict_false(pr->pr_flags & PR_REMOVE)) return (false); return (true); }