Re: [PATCH 2/5] padata: make padata_free_shell() to respect pd's ->refcnt

From: Daniel Jordan
Date: Fri Oct 28 2022 - 10:37:15 EST


On Wed, Oct 19, 2022 at 10:37:05AM +0200, Nicolai Stange wrote:
> On a PREEMPT kernel, the following has been observed while running
> pcrypt_aead01 from LTP:
>
> [ ] general protection fault: 0000 [#1] PREEMPT_RT SMP PTI
> <...>
> [ ] Workqueue: pdecrypt_parallel padata_parallel_worker
> [ ] RIP: 0010:padata_reorder+0x19/0x120
> <...>
> [ ] Call Trace:
> [ ] padata_parallel_worker+0xa3/0xf0
> [ ] process_one_work+0x1db/0x4a0
> [ ] worker_thread+0x2d/0x3c0
> [ ] ? process_one_work+0x4a0/0x4a0
> [ ] kthread+0x159/0x180
> [ ] ? kthread_park+0xb0/0xb0
> [ ] ret_from_fork+0x35/0x40
>
> The pcrypt_aead01 testcase basically runs a NEWALG/DELALG sequence for some
> fixed pcrypt instance in a loop, back to back.
>
> The problem is that once the last ->serial() in padata_serial_worker() is
> getting invoked, the pcrypt requests from the selftests would signal
> completion, and pcrypt_aead01 can move on and subsequently issue a DELALG.
> Upon pcrypt instance deregistration, the associated padata_shell would get
> destroyed, which in turn would unconditionally free the associated
> parallel_data instance.
>
> If padata_serial_worker() now resumes operation after e.g. having
> previously been preempted upon the return from the last of those ->serial()
> callbacks, its subsequent accesses to pd for managing the ->refcnt will
> all be UAFs. In particular, if the memory backing pd has meanwhile been
> reused for some new parallel_data allocation, e.g in the course of
> processing another subsequent NEWALG request, the padata_serial_worker()
> might find the initial ->refcnt of one and free pd from under that NEWALG
> or the associated selftests respectively, leading to "secondary" UAFs such
> as in the Oops above.
>
> Note that as it currently stands, a padata_shell owns a reference on its
> associated parallel_data already. So fix the UAF in padata_serial_worker()
> by making padata_free_shell() to not unconditionally free the shell's
> associated parallel_data, but to properly drop that reference via
> padata_put_pd() instead.
>
> Fixes: 07928d9bfc81 ("padata: Remove broken queue flushing")

It looks like this issue goes back to the first padata commit. For
instance, pd->refcnt goes to zero after the last _priv is serialized,
padata_free is called in another task, and a particularly sluggish
padata_reorder call touches pd after.

So wouldn't it be

Fixes: 16295bec6398 ("padata: Generic parallelization/serialization interface")

?

Otherwise,

Acked-by: Daniel Jordan <daniel.m.jordan@xxxxxxxxxx>

> Signed-off-by: Nicolai Stange <nstange@xxxxxxx>
> ---
> kernel/padata.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/padata.c b/kernel/padata.c
> index 3bd1e23f089b..0bf8c80dad5a 100644
> --- a/kernel/padata.c
> +++ b/kernel/padata.c
> @@ -1112,7 +1112,7 @@ void padata_free_shell(struct padata_shell *ps)
>
> mutex_lock(&ps->pinst->lock);
> list_del(&ps->list);
> - padata_free_pd(rcu_dereference_protected(ps->pd, 1));
> + padata_put_pd(rcu_dereference_protected(ps->pd, 1));
> mutex_unlock(&ps->pinst->lock);
>
> kfree(ps);
> --
> 2.37.3
>