{"dataType":"CVE_RECORD","dataVersion":"5.2","cveMetadata":{"cveId":"CVE-2026-63894","assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","state":"PUBLISHED","assignerShortName":"Linux","dateReserved":"2026-07-19T07:54:57.019Z","datePublished":"2026-07-19T14:55:04.882Z","dateUpdated":"2026-08-05T12:37:00.788Z"},"containers":{"cna":{"providerMetadata":{"orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","shortName":"Linux","dateUpdated":"2026-08-05T12:37:00.788Z"},"descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nusb: gadget: f_fs: serialize DMABUF cancel against request completion\n\nffs_epfile_dmabuf_io_complete() calls usb_ep_free_request() on the\ncompleted request but leaves priv->req, the back-pointer that\nffs_dmabuf_transfer() set on submission, pointing at the freed\nmemory.  A later FUNCTIONFS_DMABUF_DETACH ioctl or\nffs_epfile_release() on the close path still sees priv->req\nnon-NULL under ffs->eps_lock:\n\n    if (priv->ep && priv->req)\n            usb_ep_dequeue(priv->ep, priv->req);\n\nso usb_ep_dequeue() is called on a freed usb_request.\n\nOn dummy_hcd the dequeue path only walks a live queue and\npointer-compares, so the freed pointer reads without faulting and\nKASAN requires an explicit check at the FunctionFS call site to\nsurface the use-after-free.  On SG-capable in-tree UDCs the\ndequeue path dereferences the supplied request immediately:\n\n  * chipidea's ep_dequeue() does\n    container_of(req, struct ci_hw_req, req) and reads\n    hwreq->req.status before acquiring its own lock.\n  * cdnsp's cdnsp_gadget_ep_dequeue() reads request->status first.\n\nThe narrower option of clearing priv->req via cmpxchg() in the\ncompletion does not close the race: the completion runs without\neps_lock, so a cancel path holding eps_lock can still observe\npriv->req non-NULL, race a concurrent completion that clears and\nfrees, and pass the freed pointer to usb_ep_dequeue().  A slightly\nlonger fix that moves the free into the cleanup work is needed.\n\nSame class of lifetime race as the recent usbip-vudc timer fix [1].\n\nTake eps_lock in the sole place that mutates priv->req from the\ncallback direction by moving usb_ep_free_request() out of the\ncompletion into ffs_dmabuf_cleanup(), the existing work handler\nscheduled by ffs_dmabuf_signal_done() on\nffs->io_completion_wq.  Clear priv->req there under eps_lock\nbefore freeing, and only clear if priv->req still names our\nrequest (a subsequent ffs_dmabuf_transfer() on the same\nattachment may have queued a new one).\n\nThis keeps the existing dummy_hcd sync-dequeue invariant: the\ncompletion callback is still invoked by the UDC without\neps_lock held (dummy_hcd drops its own lock before calling the\ncallback), and the callback now takes no f_fs lock at all.\nSerialization against the cancel path happens in cleanup, which\nruns from the workqueue with no f_fs lock held on entry.\n\nThe priv ref count protects the containing ffs_dmabuf_priv:\nffs_dmabuf_transfer() takes a ref via ffs_dmabuf_get(), cleanup\ndrops it via ffs_dmabuf_put(), so priv stays live for the\ncleanup even after the cancel path's list_del + ffs_dmabuf_put.\n\nThe ffs_dmabuf_transfer() error path no longer frees usb_req\ninline: fence->req and fence->ep are set before usb_ep_queue(),\nso ffs_dmabuf_cleanup() (scheduled by the error-path\nffs_dmabuf_signal_done()) owns the free regardless of whether\nthe queue succeeded.\n\nReproduced under KASAN on both detach and close paths against\ndummy_hcd with an observability hook\n(kasan_check_byte(priv->req) immediately before usb_ep_dequeue)\nat the two FunctionFS cancel sites to surface the stale-pointer\naccess; the hook is not part of this patch.  The KASAN\nallocator / free stacks in the captured splats identify the\nsame request: alloc in dummy_alloc_request, free in\ndummy_timer, fault reached from ffs_epfile_release (close) and\nfrom the FUNCTIONFS_DMABUF_DETACH ioctl (detach).  With the\npatch applied, both paths are silent under the same hook.\n\nThe bug is reached from the FunctionFS device node, which in\nreal deployments is owned by the privileged gadget daemon\n(adbd, UMS, composite gadget services, etc.); it is not\nreachable from unprivileged userspace or from a USB host on the\ncable.  FunctionFS mounts default to GLOBAL_ROOT_UID, but the\nfilesystem supports uid=, gid=, and fmode= delegation to a\nnon-root gadget daemon, so on real deployments the attacker may\nbe a less-privileged service rather than root."}],"metrics":[{"cvssV3_1":{"version":"3.1","vectorString":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","baseScore":7.8,"baseSeverity":"HIGH"},"scenarios":[{"lang":"en","value":"AV:L - Exploitation requires local access to FunctionFS endpoint device nodes via FUNCTIONFS_DMABUF_DETACH/FUNCTIONFS_DMABUF_TRANSFER ioctls or close(), not USB host traffic or physical cable manipulation; the fix commit explicitly states it is unreachable from a remote USB host.\nAC:L - The attacker controls both sides of the race by concurrently issuing DMABUF transfer and detach/close ioctls from threads they own, and can retry until the completion/cancel window is hit.\nPR:L - While mounts default to root, FunctionFS supports uid=/gid=/fmode= delegation to non-root gadget daemons (adbd, MTP, composite services), and the fix commit identifies a less-privileged local service as the realistic attacker on deployed systems.\nUI:N - No victim action is required; the attacker triggers the vulnerability through their own ioctl and close calls racing transfer completion.\nS:U - Impact is kernel memory corruption and privilege escalation within the host kernel security boundary, not a cross-authority escape such as VM escape or IOMMU bypass.\nC:H - A use-after-free on usb_request is passed to usb_ep_dequeue(), and SG-capable in-tree UDCs (chipidea, cdnsp) dereference the freed request immediately, enabling arbitrary kernel memory reads.\nI:H - The UAF passes a dangling usb_request into UDC dequeue paths that perform container_of and structure field writes on the freed object, providing a standard heap corruption primitive for arbitrary write or code execution.\nA:H - Passing a freed usb_request into usb_ep_dequeue() on real UDC hardware causes immediate invalid dereferences and kernel oops/panic, and UAF exploitation paths also enable denial of service."}]}],"affected":[{"product":"Linux","vendor":"Linux","defaultStatus":"unaffected","repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["drivers/usb/gadget/function/f_fs.c"],"versions":[{"version":"7b07a2a7ca02a20124b552be96c5a56910795488","lessThan":"c872d8a065b3b499ce4c3ad168b5d34b68524f66","status":"affected","versionType":"git"},{"version":"7b07a2a7ca02a20124b552be96c5a56910795488","lessThan":"c7d421123b98d5e9c1c84bd9957aba36f1cbb4ca","status":"affected","versionType":"git"},{"version":"7b07a2a7ca02a20124b552be96c5a56910795488","lessThan":"552dae28dbeb5f7c4fafcda43962dc46569f58a0","status":"affected","versionType":"git"},{"version":"7b07a2a7ca02a20124b552be96c5a56910795488","lessThan":"2796646f6d892c1eb6818c7ca41fdfa12568e8d1","status":"affected","versionType":"git"}]},{"product":"Linux","vendor":"Linux","defaultStatus":"affected","repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["drivers/usb/gadget/function/f_fs.c"],"versions":[{"version":"6.9","status":"affected"},{"version":"0","lessThan":"6.9","status":"unaffected","versionType":"semver"},{"version":"6.12.93","lessThanOrEqual":"6.12.*","status":"unaffected","versionType":"semver"},{"version":"6.18.35","lessThanOrEqual":"6.18.*","status":"unaffected","versionType":"semver"},{"version":"7.0.12","lessThanOrEqual":"7.0.*","status":"unaffected","versionType":"semver"},{"version":"7.1","lessThanOrEqual":"*","status":"unaffected","versionType":"original_commit_for_fix"}]}],"cpeApplicability":[{"nodes":[{"operator":"OR","negate":false,"cpeMatch":[{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.9","versionEndExcluding":"6.12.93"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.9","versionEndExcluding":"6.18.35"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.9","versionEndExcluding":"7.0.12"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.9","versionEndExcluding":"7.1"}]}]}],"references":[{"url":"https://git.kernel.org/stable/c/c872d8a065b3b499ce4c3ad168b5d34b68524f66"},{"url":"https://git.kernel.org/stable/c/c7d421123b98d5e9c1c84bd9957aba36f1cbb4ca"},{"url":"https://git.kernel.org/stable/c/552dae28dbeb5f7c4fafcda43962dc46569f58a0"},{"url":"https://git.kernel.org/stable/c/2796646f6d892c1eb6818c7ca41fdfa12568e8d1"}],"title":"usb: gadget: f_fs: serialize DMABUF cancel against request completion","x_generator":{"engine":"bippy-1.2.0"}}}}