{"api_version":"1","generated_at":"2026-07-22T06:01:35+00:00","cve":"CVE-2026-63894","urls":{"html":"https://cve.report/CVE-2026-63894","api":"https://cve.report/api/cve/CVE-2026-63894.json","docs":"https://cve.report/api","cve_org":"https://www.cve.org/CVERecord?id=CVE-2026-63894","nvd":"https://nvd.nist.gov/vuln/detail/CVE-2026-63894"},"summary":{"title":"usb: gadget: f_fs: serialize DMABUF cancel against request completion","description":"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.","state":"PUBLISHED","assigner":"Linux","published_at":"2026-07-19 16:17:06","updated_at":"2026-07-20 15:16:54"},"problem_types":[],"metrics":[{"version":"3.1","source":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","type":"Secondary","score":"7.8","severity":"HIGH","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","data":{"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","attackVector":"LOCAL","attackComplexity":"LOW","privilegesRequired":"LOW","userInteraction":"NONE","scope":"UNCHANGED","confidentialityImpact":"HIGH","integrityImpact":"HIGH","availabilityImpact":"HIGH"}},{"version":"3.1","source":"CNA","type":"DECLARED","score":"7.8","severity":"HIGH","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","data":{"baseScore":7.8,"baseSeverity":"HIGH","vectorString":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","version":"3.1"}}],"references":[{"url":"https://git.kernel.org/stable/c/552dae28dbeb5f7c4fafcda43962dc46569f58a0","name":"https://git.kernel.org/stable/c/552dae28dbeb5f7c4fafcda43962dc46569f58a0","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/c7d421123b98d5e9c1c84bd9957aba36f1cbb4ca","name":"https://git.kernel.org/stable/c/c7d421123b98d5e9c1c84bd9957aba36f1cbb4ca","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/c872d8a065b3b499ce4c3ad168b5d34b68524f66","name":"https://git.kernel.org/stable/c/c872d8a065b3b499ce4c3ad168b5d34b68524f66","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/2796646f6d892c1eb6818c7ca41fdfa12568e8d1","name":"https://git.kernel.org/stable/c/2796646f6d892c1eb6818c7ca41fdfa12568e8d1","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-63894","name":"CVE Program record","refsource":"CVE.ORG","tags":["canonical"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-63894","name":"NVD vulnerability detail","refsource":"NVD","tags":["canonical","analysis"]}],"affected":[{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 7b07a2a7ca02a20124b552be96c5a56910795488 c872d8a065b3b499ce4c3ad168b5d34b68524f66 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 7b07a2a7ca02a20124b552be96c5a56910795488 c7d421123b98d5e9c1c84bd9957aba36f1cbb4ca git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 7b07a2a7ca02a20124b552be96c5a56910795488 552dae28dbeb5f7c4fafcda43962dc46569f58a0 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 7b07a2a7ca02a20124b552be96c5a56910795488 2796646f6d892c1eb6818c7ca41fdfa12568e8d1 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 6.9","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.9 semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.12.93 6.12.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.18.35 6.18.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.0.12 7.0.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.1 * original_commit_for_fix","platforms":[]}],"timeline":[],"solutions":[],"workarounds":[],"exploits":[],"credits":[],"nvd_cpes":[],"vendor_comments":[],"enrichments":{"kev":null,"epss":{"cve_year":"2026","cve_id":"63894","cve":"CVE-2026-63894","epss":"0.002000000","percentile":"0.100180000","score_date":"2026-07-20","updated_at":"2026-07-21 00:13:14"},"legacy_qids":[]},"source_records":{"cve_program":{"containers":{"cna":{"affected":[{"defaultStatus":"unaffected","product":"Linux","programFiles":["drivers/usb/gadget/function/f_fs.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"lessThan":"c872d8a065b3b499ce4c3ad168b5d34b68524f66","status":"affected","version":"7b07a2a7ca02a20124b552be96c5a56910795488","versionType":"git"},{"lessThan":"c7d421123b98d5e9c1c84bd9957aba36f1cbb4ca","status":"affected","version":"7b07a2a7ca02a20124b552be96c5a56910795488","versionType":"git"},{"lessThan":"552dae28dbeb5f7c4fafcda43962dc46569f58a0","status":"affected","version":"7b07a2a7ca02a20124b552be96c5a56910795488","versionType":"git"},{"lessThan":"2796646f6d892c1eb6818c7ca41fdfa12568e8d1","status":"affected","version":"7b07a2a7ca02a20124b552be96c5a56910795488","versionType":"git"}]},{"defaultStatus":"affected","product":"Linux","programFiles":["drivers/usb/gadget/function/f_fs.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"status":"affected","version":"6.9"},{"lessThan":"6.9","status":"unaffected","version":"0","versionType":"semver"},{"lessThanOrEqual":"6.12.*","status":"unaffected","version":"6.12.93","versionType":"semver"},{"lessThanOrEqual":"6.18.*","status":"unaffected","version":"6.18.35","versionType":"semver"},{"lessThanOrEqual":"7.0.*","status":"unaffected","version":"7.0.12","versionType":"semver"},{"lessThanOrEqual":"*","status":"unaffected","version":"7.1","versionType":"original_commit_for_fix"}]}],"cpeApplicability":[{"nodes":[{"cpeMatch":[{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.12.93","versionStartIncluding":"6.9","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.18.35","versionStartIncluding":"6.9","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.0.12","versionStartIncluding":"6.9","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.1","versionStartIncluding":"6.9","vulnerable":true}],"negate":false,"operator":"OR"}]}],"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":{"baseScore":7.8,"baseSeverity":"HIGH","vectorString":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","version":"3.1"}}],"providerMetadata":{"dateUpdated":"2026-07-20T13:41:17.296Z","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","shortName":"Linux"},"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"}}},"cveMetadata":{"assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","assignerShortName":"Linux","cveId":"CVE-2026-63894","datePublished":"2026-07-19T14:55:04.882Z","dateReserved":"2026-07-19T07:54:57.019Z","dateUpdated":"2026-07-20T13:41:17.296Z","state":"PUBLISHED"},"dataType":"CVE_RECORD","dataVersion":"5.2"},"nvd":{"publishedDate":"2026-07-19 16:17:06","lastModifiedDate":"2026-07-20 15:16:54","problem_types":[],"metrics":{"cvssMetricV31":[{"source":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","type":"Secondary","cvssData":{"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","attackVector":"LOCAL","attackComplexity":"LOW","privilegesRequired":"LOW","userInteraction":"NONE","scope":"UNCHANGED","confidentialityImpact":"HIGH","integrityImpact":"HIGH","availabilityImpact":"HIGH"},"exploitabilityScore":1.8,"impactScore":5.9}]},"configurations":[]},"legacy_mitre":{"record":{"CveYear":"2026","CveId":"63894","Ordinal":"1","Title":"usb: gadget: f_fs: serialize DMABUF cancel against request compl","CVE":"CVE-2026-63894","Year":"2026"},"notes":[{"CveYear":"2026","CveId":"63894","Ordinal":"1","NoteData":"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.","Type":"Description","Title":"usb: gadget: f_fs: serialize DMABUF cancel against request compl"}]}}}