{"api_version":"1","generated_at":"2026-11-06T18:30:48+00:00","cve":"CVE-2026-99231","urls":{"html":"https://cve.report/CVE-2026-99231","api":"https://cve.report/api/cve/CVE-2026-99231.json","docs":"https://cve.report/api","cve_org":"https://www.cve.org/CVERecord?id=CVE-2026-99231","nvd":"https://nvd.nist.gov/vuln/detail/CVE-2026-99231"},"summary":{"title":"io_uring/waitid: own the request before clearing ->head","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nio_uring/waitid: own the request before clearing ->head\n\nio_waitid_remove_wq() only takes the wait queue lock if ->head is set.\nio_waitid_wait() clears ->head first thing, before it tries to become the\nowner of the request by bumping ->refs. If somebody else owns the request\nat that point, nothing keeps it from completing the request while the\nwake up handler is still going to touch it:\n\nCPU0 (io_waitid_wait)                 CPU1 (io_waitid, holds a ref)\nlist_del_init(&wait->entry)\nsmp_store_release(&iw->head, NULL)\n                                      __do_wait() finds another child\n                                      io_waitid_remove_wq()\n                                        // ->head == NULL, no locking\n                                      io_waitid_finish(), IOU_COMPLETE\n                                      // request freed and reused\natomic_fetch_inc(&iw->refs)\n\nThe same goes for __io_waitid_cancel() if it claims the request in that\nwindow. ->refs is part of the per opcode data of the request, so this\nincrements whatever the next user of the request keeps there. If that\nhappens to be zero, the handler concludes that it owns the request, and\nqueues io_waitid_cb() for it. With the window held open by a delay, and\nthe request reused for a receive that is waiting for data:\n\n  BUG: KASAN: slab-use-after-free in __wake_up_common+0x1ca/0x1f0\n   __wake_up_sync_key+0x36/0x50\n   sock_def_readable+0xf1/0x420\n   unix_stream_sendmsg+0x60b/0x1110\n  Allocated by task 87:\n   io_arm_apoll+0x1ed/0x940\n  Freed by task 87:\n   __io_submit_flush_completions+0x118d/0x1a10\n   tctx_task_work_run+0x327/0x950\n\nOnly clear ->head once the handler owns the request. If it doesn't, the\nowner finds ->head set and has to wait for the lock, which is held by the\ncaller of the handler. Deleting the already deleted entry again is\nharmless.","state":"PUBLISHED","assigner":"Linux","published_at":"2026-11-02 16:17:48","updated_at":"2026-11-06 18:18:33"},"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/c254eec4364b9f4b4cbdce43be3fb4df5a651a70","name":"https://git.kernel.org/stable/c/c254eec4364b9f4b4cbdce43be3fb4df5a651a70","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/c10ad2813e15186fb51b37a4d17fafddc99e89a5","name":"https://git.kernel.org/stable/c/c10ad2813e15186fb51b37a4d17fafddc99e89a5","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/6e11ce129b34fb9a43b2bbe838df49ea52878d5c","name":"https://git.kernel.org/stable/c/6e11ce129b34fb9a43b2bbe838df49ea52878d5c","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/bf01da1a9cca4a54e5c30b8344e52c26afb87be7","name":"https://git.kernel.org/stable/c/bf01da1a9cca4a54e5c30b8344e52c26afb87be7","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://git.kernel.org/stable/c/8381a912c65b8de16e8a848225c7c5a56b6ea834","name":"https://git.kernel.org/stable/c/8381a912c65b8de16e8a848225c7c5a56b6ea834","refsource":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","tags":[],"title":"","mime":"","httpstatus":"","archivestatus":"0"},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-99231","name":"CVE Program record","refsource":"CVE.ORG","tags":["canonical"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-99231","name":"NVD vulnerability detail","refsource":"NVD","tags":["canonical","analysis"]}],"affected":[{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 09951a4d37e33b0197afc7fb77e39f09a3213ee6 bf01da1a9cca4a54e5c30b8344e52c26afb87be7 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 934ffdd1cdef232eb125e63c2ebafb7f2deff6af 8381a912c65b8de16e8a848225c7c5a56b6ea834 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected ab673c1bcaf20ac70352eeb6bf5b828462676693 c10ad2813e15186fb51b37a4d17fafddc99e89a5 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected ab673c1bcaf20ac70352eeb6bf5b828462676693 6e11ce129b34fb9a43b2bbe838df49ea52878d5c git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected ab673c1bcaf20ac70352eeb6bf5b828462676693 c254eec4364b9f4b4cbdce43be3fb4df5a651a70 git","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 6.12.113 6.12.115 semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 6.18.52 6.18.60 semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"affected 6.19","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.19 semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.12.115 6.12.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 6.18.60 6.18.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.2.14 7.2.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.3.2 7.3.* semver","platforms":[]},{"source":"CNA","vendor":"Linux","product":"Linux","version":"unaffected 7.4-rc1 * original_commit_for_fix","platforms":[]}],"timeline":[],"solutions":[],"workarounds":[],"exploits":[],"credits":[],"nvd_cpes":[],"vendor_comments":[],"enrichments":{"kev":null,"epss":{"cve_year":"2026","cve_id":"99231","cve":"CVE-2026-99231","epss":"0.001150000","percentile":"0.015780000","score_date":"2026-11-03","updated_at":"2026-11-04 00:14:34"},"legacy_qids":[]},"source_records":{"cve_program":{"containers":{"cna":{"affected":[{"defaultStatus":"unaffected","product":"Linux","programFiles":["io_uring/waitid.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"lessThan":"bf01da1a9cca4a54e5c30b8344e52c26afb87be7","status":"affected","version":"09951a4d37e33b0197afc7fb77e39f09a3213ee6","versionType":"git"},{"lessThan":"8381a912c65b8de16e8a848225c7c5a56b6ea834","status":"affected","version":"934ffdd1cdef232eb125e63c2ebafb7f2deff6af","versionType":"git"},{"lessThan":"c10ad2813e15186fb51b37a4d17fafddc99e89a5","status":"affected","version":"ab673c1bcaf20ac70352eeb6bf5b828462676693","versionType":"git"},{"lessThan":"6e11ce129b34fb9a43b2bbe838df49ea52878d5c","status":"affected","version":"ab673c1bcaf20ac70352eeb6bf5b828462676693","versionType":"git"},{"lessThan":"c254eec4364b9f4b4cbdce43be3fb4df5a651a70","status":"affected","version":"ab673c1bcaf20ac70352eeb6bf5b828462676693","versionType":"git"},{"lessThan":"6.12.115","status":"affected","version":"6.12.113","versionType":"semver"},{"lessThan":"6.18.60","status":"affected","version":"6.18.52","versionType":"semver"}]},{"defaultStatus":"affected","product":"Linux","programFiles":["io_uring/waitid.c"],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","vendor":"Linux","versions":[{"status":"affected","version":"6.19"},{"lessThan":"6.19","status":"unaffected","version":"0","versionType":"semver"},{"lessThanOrEqual":"6.12.*","status":"unaffected","version":"6.12.115","versionType":"semver"},{"lessThanOrEqual":"6.18.*","status":"unaffected","version":"6.18.60","versionType":"semver"},{"lessThanOrEqual":"7.2.*","status":"unaffected","version":"7.2.14","versionType":"semver"},{"lessThanOrEqual":"7.3.*","status":"unaffected","version":"7.3.2","versionType":"semver"},{"lessThanOrEqual":"*","status":"unaffected","version":"7.4-rc1","versionType":"original_commit_for_fix"}]}],"cpeApplicability":[{"nodes":[{"cpeMatch":[{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.12.115","versionStartIncluding":"6.12.113","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"6.18.60","versionStartIncluding":"6.18.52","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.2.14","versionStartIncluding":"6.19","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.3.2","versionStartIncluding":"6.19","vulnerable":true},{"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionEndExcluding":"7.4-rc1","versionStartIncluding":"6.19","vulnerable":true}],"negate":false,"operator":"OR"}]}],"descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nio_uring/waitid: own the request before clearing ->head\n\nio_waitid_remove_wq() only takes the wait queue lock if ->head is set.\nio_waitid_wait() clears ->head first thing, before it tries to become the\nowner of the request by bumping ->refs. If somebody else owns the request\nat that point, nothing keeps it from completing the request while the\nwake up handler is still going to touch it:\n\nCPU0 (io_waitid_wait)                 CPU1 (io_waitid, holds a ref)\nlist_del_init(&wait->entry)\nsmp_store_release(&iw->head, NULL)\n                                      __do_wait() finds another child\n                                      io_waitid_remove_wq()\n                                        // ->head == NULL, no locking\n                                      io_waitid_finish(), IOU_COMPLETE\n                                      // request freed and reused\natomic_fetch_inc(&iw->refs)\n\nThe same goes for __io_waitid_cancel() if it claims the request in that\nwindow. ->refs is part of the per opcode data of the request, so this\nincrements whatever the next user of the request keeps there. If that\nhappens to be zero, the handler concludes that it owns the request, and\nqueues io_waitid_cb() for it. With the window held open by a delay, and\nthe request reused for a receive that is waiting for data:\n\n  BUG: KASAN: slab-use-after-free in __wake_up_common+0x1ca/0x1f0\n   __wake_up_sync_key+0x36/0x50\n   sock_def_readable+0xf1/0x420\n   unix_stream_sendmsg+0x60b/0x1110\n  Allocated by task 87:\n   io_arm_apoll+0x1ed/0x940\n  Freed by task 87:\n   __io_submit_flush_completions+0x118d/0x1a10\n   tctx_task_work_run+0x327/0x950\n\nOnly clear ->head once the handler owns the request. If it doesn't, the\nowner finds ->head set and has to wait for the lock, which is held by the\ncaller of the handler. Deleting the already deleted entry again is\nharmless."}],"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"},"scenarios":[{"lang":"en","value":"AV:L - io_uring_enter() submits an IORING_OP_WAITID SQE via io_submit_sqes()->io_init_req()->io_waitid_prep()/io_queue_sqe()->io_issue_sqe()->io_waitid(), which arms io_waitid_wait() on current->signal->wait_chldexit; the trigger is that local SQE plus the attacker’s own child exit through __wake_up_parent(), not a remote protocol message.\nAC:L - The attacker owns both sides of the race in io_waitid_wait(): they fork children so __wake_up_parent() runs the callback while io_waitid() still holds the issue ref in __do_wait(), or another thread’s IORING_OP_ASYNC_CANCEL claims the request in __io_waitid_cancel(), then recycle the freed io_kiocb from the request cache with a follow-up recv SQE.\nPR:L - io_uring_setup() calls io_uring_allowed(), and sysctl_io_uring_disabled defaults to 0 so no capability is required; IORING_OP_WAITID’s io_issue_def has no extra privilege flag, and kernel_waitid_prepare() only looks up the caller’s own children.\nUI:N - The same local process creates the ring, submits IORING_OP_WAITID, forks and exits its own children to wake wait_chldexit, and optionally submits IORING_OP_ASYNC_CANCEL; no other user must act.\nS:U - The use-after-free is of an io_kiocb in the host io_uring request cache; impact stays inside the same kernel authority and does not cross a VM, IOMMU, or guest/host boundary.\nC:H - After io_waitid_finish() frees the request, io_waitid_wait() still does atomic_fetch_inc(&iw->refs) on the cmd overlay of the recycled io_kiocb; occupying it with IORING_OP_RECV (as in the commit’s KASAN trace) and queueing io_waitid_cb() on that object yields a kernel read of the reused request.\nI:H - The same UAF lets io_waitid_wait() write req->io_task_work.func=io_waitid_cb on a request already reused as a receive, so io_waitid_complete() frees an in-flight poll wait; the commit’s KASAN hit in __wake_up_common() from unix_stream_sendmsg() is a type-confused write/control-flow primitive.\nA:H - Completing and freeing the io_kiocb while io_waitid_wait() still touches iw->refs, or running io_waitid_cb() on a recycled receive, produces the documented KASAN slab-use-after-free in __wake_up_common() and a kernel oops."}]}],"providerMetadata":{"dateUpdated":"2026-11-06T17:15:12.226Z","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","shortName":"Linux"},"references":[{"url":"https://git.kernel.org/stable/c/bf01da1a9cca4a54e5c30b8344e52c26afb87be7"},{"url":"https://git.kernel.org/stable/c/8381a912c65b8de16e8a848225c7c5a56b6ea834"},{"url":"https://git.kernel.org/stable/c/c10ad2813e15186fb51b37a4d17fafddc99e89a5"},{"url":"https://git.kernel.org/stable/c/6e11ce129b34fb9a43b2bbe838df49ea52878d5c"},{"url":"https://git.kernel.org/stable/c/c254eec4364b9f4b4cbdce43be3fb4df5a651a70"}],"title":"io_uring/waitid: own the request before clearing ->head","x_generator":{"engine":"bippy-1.2.0"}}},"cveMetadata":{"assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","assignerShortName":"Linux","cveId":"CVE-2026-99231","datePublished":"2026-11-02T16:06:38.553Z","dateReserved":"2026-09-25T10:25:14.402Z","dateUpdated":"2026-11-06T17:15:12.226Z","state":"PUBLISHED"},"dataType":"CVE_RECORD","dataVersion":"5.2"},"nvd":{"publishedDate":"2026-11-02 16:17:48","lastModifiedDate":"2026-11-06 18:18:33","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":"99231","Ordinal":"1","Title":"io_uring/waitid: own the request before clearing ->head","CVE":"CVE-2026-99231","Year":"2026"},"notes":[{"CveYear":"2026","CveId":"99231","Ordinal":"1","NoteData":"In the Linux kernel, the following vulnerability has been resolved:\n\nio_uring/waitid: own the request before clearing ->head\n\nio_waitid_remove_wq() only takes the wait queue lock if ->head is set.\nio_waitid_wait() clears ->head first thing, before it tries to become the\nowner of the request by bumping ->refs. If somebody else owns the request\nat that point, nothing keeps it from completing the request while the\nwake up handler is still going to touch it:\n\nCPU0 (io_waitid_wait)                 CPU1 (io_waitid, holds a ref)\nlist_del_init(&wait->entry)\nsmp_store_release(&iw->head, NULL)\n                                      __do_wait() finds another child\n                                      io_waitid_remove_wq()\n                                        // ->head == NULL, no locking\n                                      io_waitid_finish(), IOU_COMPLETE\n                                      // request freed and reused\natomic_fetch_inc(&iw->refs)\n\nThe same goes for __io_waitid_cancel() if it claims the request in that\nwindow. ->refs is part of the per opcode data of the request, so this\nincrements whatever the next user of the request keeps there. If that\nhappens to be zero, the handler concludes that it owns the request, and\nqueues io_waitid_cb() for it. With the window held open by a delay, and\nthe request reused for a receive that is waiting for data:\n\n  BUG: KASAN: slab-use-after-free in __wake_up_common+0x1ca/0x1f0\n   __wake_up_sync_key+0x36/0x50\n   sock_def_readable+0xf1/0x420\n   unix_stream_sendmsg+0x60b/0x1110\n  Allocated by task 87:\n   io_arm_apoll+0x1ed/0x940\n  Freed by task 87:\n   __io_submit_flush_completions+0x118d/0x1a10\n   tctx_task_work_run+0x327/0x950\n\nOnly clear ->head once the handler owns the request. If it doesn't, the\nowner finds ->head set and has to wait for the lock, which is held by the\ncaller of the handler. Deleting the already deleted entry again is\nharmless.","Type":"Description","Title":"io_uring/waitid: own the request before clearing ->head"}]}}}