Daniel Jordan <daniel.m.jordan@xxxxxxxxxx> writes: > On Wed, Oct 19, 2022 at 10:37:06AM +0200, Nicolai Stange wrote: >> On entry of padata_do_serial(), the in-flight padata_priv owns a reference >> to the associated parallel_data instance. >> >> However, as soon as the padata_priv got enqueued on the reorder list, it >> can be completed from a different context, causing the reference to get >> released in the course. >> >> This would potentially cause UAFs from the subsequent padata_reorder() >> operations invoked from the enqueueing padata_do_serial() or from the >> reorder work. >> <snip> >> --- >> kernel/padata.c | 26 +++++++++++++++++++++----- >> 1 file changed, 21 insertions(+), 5 deletions(-) >> >> diff --git a/kernel/padata.c b/kernel/padata.c >> index 0bf8c80dad5a..b79226727ef7 100644 >> --- a/kernel/padata.c >> +++ b/kernel/padata.c >> @@ -275,7 +275,7 @@ static struct padata_priv *padata_find_next(struct parallel_data *pd, >> return padata; >> } >> >> -static void padata_reorder(struct parallel_data *pd) >> +static bool padata_reorder(struct parallel_data *pd) >> { >> struct padata_instance *pinst = pd->ps->pinst; >> int cb_cpu; >> @@ -294,7 +294,7 @@ static void padata_reorder(struct parallel_data *pd) >> * care for all the objects enqueued during the holdtime of the lock. >> */ >> if (!spin_trylock_bh(&pd->lock)) >> - return; >> + return false; >> >> while (1) { >> padata = padata_find_next(pd, true); >> @@ -331,17 +331,23 @@ static void padata_reorder(struct parallel_data *pd) >> >> reorder = per_cpu_ptr(pd->reorder_list, pd->cpu); >> if (!list_empty(&reorder->list) && padata_find_next(pd, false)) >> - queue_work(pinst->serial_wq, &pd->reorder_work); >> + return queue_work(pinst->serial_wq, &pd->reorder_work); >> + >> + return false; >> } >> >> static void invoke_padata_reorder(struct work_struct *work) >> { >> struct parallel_data *pd; >> + bool keep_refcnt; >> >> local_bh_disable(); >> pd = container_of(work, struct parallel_data, reorder_work); >> - padata_reorder(pd); >> + keep_refcnt = padata_reorder(pd); >> local_bh_enable(); >> + >> + if (!keep_refcnt) >> + padata_put_pd(pd); >> } >> >> static void padata_serial_worker(struct work_struct *serial_work) >> @@ -392,6 +398,15 @@ void padata_do_serial(struct padata_priv *padata) >> struct padata_list *reorder = per_cpu_ptr(pd->reorder_list, hashed_cpu); >> struct padata_priv *cur; >> >> + /* >> + * The in-flight padata owns a reference on pd. However, as >> + * soon as it's been enqueued on the reorder list, another >> + * task can dequeue and complete it, thereby dropping the >> + * reference. Grab another reference here, it will eventually >> + * be released from a reorder work, if any, or below. >> + */ >> + padata_get_pd(pd); >> + >> spin_lock(&reorder->lock); >> /* Sort in ascending order of sequence number. */ >> list_for_each_entry_reverse(cur, &reorder->list, list) >> @@ -407,7 +422,8 @@ void padata_do_serial(struct padata_priv *padata) >> */ >> smp_mb(); >> >> - padata_reorder(pd); >> + if (!padata_reorder(pd)) >> + padata_put_pd(pd); > > do_serial is supposed to be called with BHs disabled and will be in > every case after a fix for a separate issue that I plan to send this > cycle. Given that it (will soon...) always happen under RCU protection, > part of this issue could be addressed like this, which puts the expense > of dealing with this rare problem in the slow path: > > diff --git a/kernel/padata.c b/kernel/padata.c > index 0bf8c80dad5a..cd6740ae6629 100644 > --- a/kernel/padata.c > +++ b/kernel/padata.c > @@ -1110,6 +1110,12 @@ void padata_free_shell(struct padata_shell *ps) > if (!ps) > return; > > + /* > + * Wait for all _do_serial calls to finish to avoid touching freed pd's > + * and ps's. > + */ > + synchronize_rcu(); > + > mutex_lock(&ps->pinst->lock); > list_del(&ps->list); > padata_put_pd(rcu_dereference_protected(ps->pd, 1)); > > pcrypt calls padata_free_shell() when all outstanding transforms (and > thus requests, I think) have been freed/completed, so no new task can > come into padata_reorder. synchronize_rcu() then flushes out any > remaining _reorder calls. > > This doesn't deal with pending reorder_work items, but we can fix it > later in the series. > > What do you think? Yes, I think that would work. Will you handle it alongside that fix for a separate issue you mentioned above? Or shall I once this fix has landed? Thanks! Nicolai -- SUSE Software Solutions Germany GmbH, Frankenstraße 146, 90461 Nürnberg, Germany GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman (HRB 36809, AG Nürnberg)