PT-2026-61211 · Linux · Linux
CVE-2026-63894
·
Published
2026-07-19
·
Updated
2026-07-19
CVSS v3.1
7.8
High
| Vector | AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
In the Linux kernel, the following vulnerability has been resolved:
usb: gadget: f fs: serialize DMABUF cancel against request completion
ffs epfile dmabuf io complete() calls usb ep free request() on the
completed request but leaves priv->req, the back-pointer that
ffs dmabuf transfer() set on submission, pointing at the freed
memory. A later FUNCTIONFS DMABUF DETACH ioctl or
ffs epfile release() on the close path still sees priv->req
non-NULL under ffs->eps lock:
if (priv->ep && priv->req)
usb ep dequeue(priv->ep, priv->req);
so usb ep dequeue() is called on a freed usb request.
On dummy hcd the dequeue path only walks a live queue and
pointer-compares, so the freed pointer reads without faulting and
KASAN requires an explicit check at the FunctionFS call site to
surface the use-after-free. On SG-capable in-tree UDCs the
dequeue path dereferences the supplied request immediately:
- chipidea's ep dequeue() does container of(req, struct ci hw req, req) and reads hwreq->req.status before acquiring its own lock.
- cdnsp's cdnsp gadget ep dequeue() reads request->status first.
The narrower option of clearing priv->req via cmpxchg() in the
completion does not close the race: the completion runs without
eps lock, so a cancel path holding eps lock can still observe
priv->req non-NULL, race a concurrent completion that clears and
frees, and pass the freed pointer to usb ep dequeue(). A slightly
longer fix that moves the free into the cleanup work is needed.
Same class of lifetime race as the recent usbip-vudc timer fix [1].
Take eps lock in the sole place that mutates priv->req from the
callback direction by moving usb ep free request() out of the
completion into ffs dmabuf cleanup(), the existing work handler
scheduled by ffs dmabuf signal done() on
ffs->io completion wq. Clear priv->req there under eps lock
before freeing, and only clear if priv->req still names our
request (a subsequent ffs dmabuf transfer() on the same
attachment may have queued a new one).
This keeps the existing dummy hcd sync-dequeue invariant: the
completion callback is still invoked by the UDC without
eps lock held (dummy hcd drops its own lock before calling the
callback), and the callback now takes no f fs lock at all.
Serialization against the cancel path happens in cleanup, which
runs from the workqueue with no f fs lock held on entry.
The priv ref count protects the containing ffs dmabuf priv:
ffs dmabuf transfer() takes a ref via ffs dmabuf get(), cleanup
drops it via ffs dmabuf put(), so priv stays live for the
cleanup even after the cancel path's list del + ffs dmabuf put.
The ffs dmabuf transfer() error path no longer frees usb req
inline: fence->req and fence->ep are set before usb ep queue(),
so ffs dmabuf cleanup() (scheduled by the error-path
ffs dmabuf signal done()) owns the free regardless of whether
the queue succeeded.
Reproduced under KASAN on both detach and close paths against
dummy hcd with an observability hook
(kasan check byte(priv->req) immediately before usb ep dequeue)
at the two FunctionFS cancel sites to surface the stale-pointer
access; the hook is not part of this patch. The KASAN
allocator / free stacks in the captured splats identify the
same request: alloc in dummy alloc request, free in
dummy timer, fault reached from ffs epfile release (close) and
from the FUNCTIONFS DMABUF DETACH ioctl (detach). With the
patch applied, both paths are silent under the same hook.
The bug is reached from the FunctionFS device node, which in
real deployments is owned by the privileged gadget daemon
(adbd, UMS, composite gadget services, etc.); it is not
reachable from unprivileged userspace or from a USB host on the
cable. FunctionFS mounts default to GLOBAL ROOT UID, but the
filesystem supports uid=, gid=, and fmode= delegation to a
non-root gadget daemon, so on real deployments the attacker may
be a less-privileged service rather than root.
Fix
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Linux