CVE-2026-63894

usb: gadget: f_fs: serialize DMABUF cancel against request completion

High · published July 19, 2026

CVSS v3.1
7.8
EPSS
0%
Percentile
6.1
In the wild
Unconfirmed
What it is

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.

The record
Technical detail
CVSS v3.1
7.8 · HIGH
Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVSS v4.0
Not supplied
EPSS
0.00170 · 6.1th percentile
Published
2026-07-19T14:55Z
EPSS history
Timeline
  • 19 JUL 14:55Z
    usb: gadget: f_fs: serialize DMABUF cancel against request completion
    cvelistv5