{"id":14945,"date":"2026-09-04T19:02:19","date_gmt":"2026-09-04T11:02:19","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-63894"},"modified":"2026-09-04T19:02:19","modified_gmt":"2026-09-04T11:02:19","slug":"cve-2026-63894","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-63894","title":{"rendered":"CVE-2026-63894 [HIGH] &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nusb: gadget: f_fs: se"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>usb: gadget: f_fs: serialize DMABUF cancel against request completion<\/p>\n<p>ffs_epfile_dmabuf_io_complete() calls usb_ep_free_request() on the<br \/>\ncompleted request but leaves priv-&gt;req, the back-pointer that<br \/>\nffs_dmabuf_transfer() set on submission, pointing at the freed<br \/>\nmemory.  A later FUNCTIONFS_DMABUF_DETACH ioctl or<br \/>\nffs_epfile_release() on the close path still sees priv-&gt;req<br \/>\nnon-NULL under ffs-&gt;eps_lock:<\/p>\n<p>    if (priv-&gt;ep &amp;&amp; priv-&gt;req)<br \/>\n            usb_ep_dequeue(priv-&gt;ep, priv-&gt;req);<\/p>\n<p>so usb_ep_dequeue() is called on a freed usb_request.<\/p>\n<p>On dummy_hcd the dequeue path only walks a live queue and<br \/>\npointer-compares, so the freed pointer reads without faulting and<br \/>\nKASAN requires an explicit check at the FunctionFS call site to<br \/>\nsurface the use-after-free.  On SG-capable in-tree UDCs the<br \/>\ndequeue path dereferences the supplied request immediately:<\/p>\n<p>  * chipidea&#8217;s ep_dequeue() does<br \/>\n    container_of(req, struct ci_hw_req, req) and reads<br \/>\n    hwreq-&gt;req.status before acquiring its own lock.<br \/>\n  * cdnsp&#8217;s cdnsp_gadget_ep_dequeue() reads request-&gt;status first.<\/p>\n<p>The narrower option of clearing priv-&gt;req via cmpxchg() in the<br \/>\ncompletion does not close the race: the completion runs without<br \/>\neps_lock, so a cancel path holding eps_lock can still observe<br \/>\npriv-&gt;req non-NULL, race a concurrent completion that clears and<br \/>\nfrees, and pass the freed pointer to usb_ep_dequeue().  A slightly<br \/>\nlonger fix that moves the free into the cleanup work is needed.<\/p>\n<p>Same class of lifetime race as the recent usbip-vudc timer fix [1].<\/p>\n<p>Take eps_lock in the sole place that mutates priv-&gt;req from the<br \/>\ncallback direction by moving usb_ep_free_request() out of the<br \/>\ncompletion into ffs_dmabuf_cleanup(), the existing work handler<br \/>\nscheduled by ffs_dmabuf_signal_done() on<br \/>\nffs-&gt;io_completion_wq.  Clear priv-&gt;req there under eps_lock<br \/>\nbefore freeing, and only clear if priv-&gt;req still names our<br \/>\nrequest (a subsequent ffs_dmabuf_transfer() on the same<br \/>\nattachment may have queued a new one).<\/p>\n<p>This keeps the existing dummy_hcd sync-dequeue invariant: the<br \/>\ncompletion callback is still invoked by the UDC without<br \/>\neps_lock held (dummy_hcd drops its own lock before calling the<br \/>\ncallback), and the callback now takes no f_fs lock at all.<br \/>\nSerialization against the cancel path happens in cleanup, which<br \/>\nruns from the workqueue with no f_fs lock held on entry.<\/p>\n<p>The priv ref count protects the containing ffs_dmabuf_priv:<br \/>\nffs_dmabuf_transfer() takes a ref via ffs_dmabuf_get(), cleanup<br \/>\ndrops it via ffs_dmabuf_put(), so priv stays live for the<br \/>\ncleanup even after the cancel path&#8217;s list_del + ffs_dmabuf_put.<\/p>\n<p>The ffs_dmabuf_transfer() error path no longer frees usb_req<br \/>\ninline: fence-&gt;req and fence-&gt;ep are set before usb_ep_queue(),<br \/>\nso ffs_dmabuf_cleanup() (scheduled by the error-path<br \/>\nffs_dmabuf_signal_done()) owns the free regardless of whether<br \/>\nthe queue succeeded.<\/p>\n<p>Reproduced under KASAN on both detach and close paths against<br \/>\ndummy_hcd with an observability hook<br \/>\n(kasan_check_byte(priv-&gt;req) immediately before usb_ep_dequeue)<br \/>\nat the two FunctionFS cancel sites to surface the stale-pointer<br \/>\naccess; the hook is not part of this patch.  The KASAN<br \/>\nallocator \/ free stacks in the captured splats identify the<br \/>\nsame request: alloc in dummy_alloc_request, free in<br \/>\ndummy_timer, fault reached from ffs_epfile_release (close) and<br \/>\nfrom the FUNCTIONFS_DMABUF_DETACH ioctl (detach).  With the<br \/>\npatch applied, both paths are silent under the same hook.<\/p>\n<p>The bug is reached from the FunctionFS device node, which in<br \/>\nreal deployments is owned by the privileged gadget daemon<br \/>\n(adbd, UMS, composite gadget services, etc.); it is not<br \/>\nreachable from unprivileged userspace or from a USB host on the<br \/>\ncable.  FunctionFS mounts default to GLOBAL_ROOT_UID, but the<br \/>\nfilesystem supports uid=, gid=, and fmode= delegation to a<br \/>\nnon-root gadget daemon, so on real deployments the attacker may<br \/>\nbe a less-privileged service rather than root.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>usb: gadget: f_fs: serialize DMABUF cancel against request completion<\/p>\n<p>ffs_epfile_dmabuf_io_complete() calls usb_ep_free_request() <\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-14945","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/14945","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=14945"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=14945"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=14945"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}