Re: [BUG] usb: gadgetfs: KASAN null-ptr-deref and intermittent UAF in ep_aio_cancel()
From: Alan Stern
Date: Thu Jul 02 2026 - 10:28:05 EST
On Thu, Jul 02, 2026 at 05:08:17PM +0900, 김민서 wrote:
> Hi Alan,
>
> Thank you for taking a look.
>
> I do not want to over-specify the exact fix, but my tentative view from
> the reproducer runs and code inspection is that the key invariant is that
> struct kiocb_priv should either remain valid while it can still be reached
> by ep_aio_cancel() for the same iocb, or it should be made unreachable from
> the cancel path in a synchronized way.
>
> In the immediate completion path I am looking at, ep_aio_complete()
> appears to clear fields in priv and then free priv before iocb->private is
> set to NULL and before iocb->ki_complete() runs. Meanwhile ep_aio_cancel()
> reads iocb->private and immediately dereferences priv->epdata.
>
> That seems to explain both symptoms I observed: a NULL-deref if
> iocb->private is already NULL, and a UAF if cancellation observes a stale
> non-NULL priv pointer.
>
> I may be missing some ordering guarantee in the AIO core, but from the
> reproducer this is the window that seems relevant. So I think a NULL check
> in ep_aio_cancel() would likely handle the repeated NULL-deref symptom,
> but may not address the stale non-NULL case. A safer direction might be to
> ensure sufficient serialization or lifetime ordering around iocb->private /
> struct kiocb_priv, so that either priv remains valid for the cancel
> callback, or cancellation is already unreachable before priv is freed for
> that iocb.
Also, NULL checks don't fix races (cancel vs. complete).
It's hard to make lifetime ordering work when you don't know whether the
aio transfer will ever be cancelled. Serialization seems like the best
solution (provided you can guarantee that it won't cause a deadlock).
However, I believe there is a valid reason why the spinlock calls in
ep_aio_cancel() are commented out, although I don't remember what it is.
Maybe because at that point in the code there is no way to know whether
epdata and epdata->dev are valid pointers?
Anyway, it would be good if you could figure out a solution and write a
patch to implement it. Perhaps a single global spinlock to protect all
the aio pathways (or all those for a particular gadget) would work.
Alan Stern