{"id":"CVE-2026-74568","state":"PUBLISHED","title":"KVM: arm64: vgic: Fix race between LPI release and re-registration","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nKVM: arm64: vgic: Fix race between LPI release and re-registration\n\nFix a potential race between decrementing an LPI's reference count and\nevicting that structure from the LPI xarray.\n\nLPI structures are maintained in the VGIC LPI xarray (dist->lpi_xa).\nWhen the reference count of an LPI structure drops to zero,\nvgic_release_lpi_locked() removes the structure from the xarray and\nfrees it under the xarray lock.\n\nHowever, the release of an LPI can race with a concurrent LPI\nre-registration with the same INTID via vgic_add_lpi() on another CPU,\nsince the reference count drop and the xarray eviction are not performed\nin a single atomic step. This can happen e.g. if the guest issues a\nDISCARD while the LPI is still referenced from a vCPU's active-pending\nlist (ap_list), and the same INTID is re-mapped via MAPTI.\n\nParticularly, vgic_release_lpi_locked() is called from two distinct\npaths: direct release via vgic_put_irq(), and deferred release via\nvgic_release_deleted_lpis(). During direct release, the issue can result\nin deleting a newly registered LPI from the xarray:\n\n  CPU0 (Releasing LPI)                    CPU1 (Adding new LPI)\n  ====================                    =====================\n  vgic_put_irq()\n      __vgic_put_irq()\n          refcount_dec_and_test()\n                                          vgic_add_lpi()\n                                              xa_lock_irqsave()\n                                              old_irq = xa_load(.., intid)\n                                              vgic_try_get_irq_ref(old_irq) == false\n                        new IRQ inserted -->  __xa_store(.., intid, ..)\n                                              xa_unlock_irqrestore()\n  xa_lock_irqsave();\n  vgic_release_lpi_locked()\n      __xa_erase(.., irq->intid)   <-- BUG: new IRQ is erased\n      kfree_rcu(old_irq)\n\nDuring the deferred release path, the old IRQ can be leaked:\n\n  CPU0 (Releasing LPI)                    CPU1 (Adding new LPI)\n  ====================                    =====================\n  vgic_put_irq_norelease()\n      __vgic_put_irq()\n          refcount_dec_and_test()\n      irq->pending_release = true\n                                          vgic_add_lpi()\n                                              xa_lock_irqsave()\n                                              old_irq = xa_load(.., intid)\n                                              vgic_try_get_irq_ref(oldirq) == false\n                 BUG: old IRQ overwritten --> __xa_store(.., intid, ..)\n                                              xa_unlock_irqrestore()\n\n  vgic_release_deleted_lpis()\n      xa_lock_irqsave()\n      xa_for_each() { .. } <-- old IRQ with pending_release = true\n                               is gone, so it cannot be released\n\nTo fix the direct release path, move the reference count drop inside\nthe xarray lock, making sure that vgic_add_lpi() never encounters the\nto-be-released LPI.\n\nIn the deferred release path, the refcount drop must happen under a raw\nspinlock, so the xarray lock cannot be grabbed, and the same solution\ndoes not work. Instead, update vgic_add_lpi(), so that if it evicts\nan LPI from the xarray, it takes on the responsibility of freeing it.\nConsequently, an LPI may now be freed concurrently after a deferred\nrelease drops the refcount, so accessing the pending_release field is no\nlonger safe from use-after-free. Delete all uses of the flag, and update\nvgic_release_deleted_lpis() to identify orphaned LPIs purely based on\ntheir refcount.","publishedAt":"2026-08-15T12:28:08.859Z","updatedAt":"2026-10-16T19:05:56.525Z","severity":{"score":9.3,"label":"Critical","version":"3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H","source":"Linux"},"assigner":"Linux","products":["Linux Linux"],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"3a08a6ca7c373198c84e2a8c025c395ee966ff8a","status":"affected","lessThan":"f8e5715a37378ea1a140ee1162fa441e149c5142","versionType":"git"},{"version":"3a08a6ca7c373198c84e2a8c025c395ee966ff8a","status":"affected","lessThan":"292e80a159aa88635bf668a7212cfdf526b8bd52","versionType":"git"},{"version":"3a08a6ca7c373198c84e2a8c025c395ee966ff8a","status":"affected","lessThan":"cbfe2b24a1ea9de35032dbdd100fdc700f5be92d","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["arch/arm64/kvm/vgic/vgic-its.c","arch/arm64/kvm/vgic/vgic.c","include/kvm/arm_vgic.h"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.17","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.17","versionType":"semver"},{"version":"6.18.57","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.1.8","status":"unaffected","lessThanOrEqual":"7.1.*","versionType":"semver"},{"version":"7.2","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["arch/arm64/kvm/vgic/vgic-its.c","arch/arm64/kvm/vgic/vgic.c","include/kvm/arm_vgic.h"]}],"references":["https://git.kernel.org/stable/c/f8e5715a37378ea1a140ee1162fa441e149c5142","https://git.kernel.org/stable/c/292e80a159aa88635bf668a7212cfdf526b8bd52","https://git.kernel.org/stable/c/cbfe2b24a1ea9de35032dbdd100fdc700f5be92d"],"weaknesses":[],"ingestedAt":"2026-10-29T20:12:58.252Z","source":{"repository":"CVEProject/cvelistV5","path":"cves/2026/74xxx/CVE-2026-74568.json","commit":"556c9d3a8cd17e218bb8e452ba7cd619a384ff47"},"raw":{"dataType":"CVE_RECORD","dataVersion":"5.2","cveMetadata":{"cveId":"CVE-2026-74568","state":"PUBLISHED","datePublished":"2026-08-15T12:28:08.859Z","dateUpdated":"2026-10-16T19:05:56.525Z","assignerShortName":"Linux","assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateReserved":"2026-08-15T05:44:03.917Z"},"containers":{"cna":{"title":"KVM: arm64: vgic: Fix race between LPI release and re-registration","descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nKVM: arm64: vgic: Fix race between LPI release and re-registration\n\nFix a potential race between decrementing an LPI's reference count and\nevicting that structure from the LPI xarray.\n\nLPI structures are maintained in the VGIC LPI xarray (dist->lpi_xa).\nWhen the reference count of an LPI structure drops to zero,\nvgic_release_lpi_locked() removes the structure from the xarray and\nfrees it under the xarray lock.\n\nHowever, the release of an LPI can race with a concurrent LPI\nre-registration with the same INTID via vgic_add_lpi() on another CPU,\nsince the reference count drop and the xarray eviction are not performed\nin a single atomic step. This can happen e.g. if the guest issues a\nDISCARD while the LPI is still referenced from a vCPU's active-pending\nlist (ap_list), and the same INTID is re-mapped via MAPTI.\n\nParticularly, vgic_release_lpi_locked() is called from two distinct\npaths: direct release via vgic_put_irq(), and deferred release via\nvgic_release_deleted_lpis(). During direct release, the issue can result\nin deleting a newly registered LPI from the xarray:\n\n  CPU0 (Releasing LPI)                    CPU1 (Adding new LPI)\n  ====================                    =====================\n  vgic_put_irq()\n      __vgic_put_irq()\n          refcount_dec_and_test()\n                                          vgic_add_lpi()\n                                              xa_lock_irqsave()\n                                              old_irq = xa_load(.., intid)\n                                              vgic_try_get_irq_ref(old_irq) == false\n                        new IRQ inserted -->  __xa_store(.., intid, ..)\n                                              xa_unlock_irqrestore()\n  xa_lock_irqsave();\n  vgic_release_lpi_locked()\n      __xa_erase(.., irq->intid)   <-- BUG: new IRQ is erased\n      kfree_rcu(old_irq)\n\nDuring the deferred release path, the old IRQ can be leaked:\n\n  CPU0 (Releasing LPI)                    CPU1 (Adding new LPI)\n  ====================                    =====================\n  vgic_put_irq_norelease()\n      __vgic_put_irq()\n          refcount_dec_and_test()\n      irq->pending_release = true\n                                          vgic_add_lpi()\n                                              xa_lock_irqsave()\n                                              old_irq = xa_load(.., intid)\n                                              vgic_try_get_irq_ref(oldirq) == false\n                 BUG: old IRQ overwritten --> __xa_store(.., intid, ..)\n                                              xa_unlock_irqrestore()\n\n  vgic_release_deleted_lpis()\n      xa_lock_irqsave()\n      xa_for_each() { .. } <-- old IRQ with pending_release = true\n                               is gone, so it cannot be released\n\nTo fix the direct release path, move the reference count drop inside\nthe xarray lock, making sure that vgic_add_lpi() never encounters the\nto-be-released LPI.\n\nIn the deferred release path, the refcount drop must happen under a raw\nspinlock, so the xarray lock cannot be grabbed, and the same solution\ndoes not work. Instead, update vgic_add_lpi(), so that if it evicts\nan LPI from the xarray, it takes on the responsibility of freeing it.\nConsequently, an LPI may now be freed concurrently after a deferred\nrelease drops the refcount, so accessing the pending_release field is no\nlonger safe from use-after-free. Delete all uses of the flag, and update\nvgic_release_deleted_lpis() to identify orphaned LPIs purely based on\ntheir refcount."}],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"3a08a6ca7c373198c84e2a8c025c395ee966ff8a","status":"affected","lessThan":"f8e5715a37378ea1a140ee1162fa441e149c5142","versionType":"git"},{"version":"3a08a6ca7c373198c84e2a8c025c395ee966ff8a","status":"affected","lessThan":"292e80a159aa88635bf668a7212cfdf526b8bd52","versionType":"git"},{"version":"3a08a6ca7c373198c84e2a8c025c395ee966ff8a","status":"affected","lessThan":"cbfe2b24a1ea9de35032dbdd100fdc700f5be92d","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["arch/arm64/kvm/vgic/vgic-its.c","arch/arm64/kvm/vgic/vgic.c","include/kvm/arm_vgic.h"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.17","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.17","versionType":"semver"},{"version":"6.18.57","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.1.8","status":"unaffected","lessThanOrEqual":"7.1.*","versionType":"semver"},{"version":"7.2","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["arch/arm64/kvm/vgic/vgic-its.c","arch/arm64/kvm/vgic/vgic.c","include/kvm/arm_vgic.h"]}],"references":[{"url":"https://git.kernel.org/stable/c/f8e5715a37378ea1a140ee1162fa441e149c5142"},{"url":"https://git.kernel.org/stable/c/292e80a159aa88635bf668a7212cfdf526b8bd52"},{"url":"https://git.kernel.org/stable/c/cbfe2b24a1ea9de35032dbdd100fdc700f5be92d"}],"metrics":[{"cvssV3_1":{"version":"3.1","vectorString":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H","baseScore":9.3,"baseSeverity":"CRITICAL"},"scenarios":[{"lang":"en","value":"AV:L - The bug is reached when a malicious KVM guest writes emulated GICv3 ITS MMIO (GITS_CWRITER), processing DISCARD/MAPTI commands that call vgic_add_lpi() and vgic_put_irq(); this requires guest code execution on the host, not remote network input.\nAC:L - The race is guest-driven and repeatable: a malicious guest can keep an LPI referenced on a vCPU ap_list, issue DISCARD, and concurrently re-map the same INTID via MAPTI across vCPUs, controlling both sides of the refcount/xarray race.\nPR:N - Exploitation requires only code inside a KVM guest on an arm64 host with VGICv3 ITS/LPI (common on cloud ARM servers); no host root, CAP_SYS_ADMIN, or other elevated host privileges are needed beyond normal VM tenancy.\nUI:N - No victim interaction is required; the attacker fully controls guest ITS command submission, interrupt injection, and multi-vCPU timing from within the VM.\nS:C - Corrupting host-kernel VGIC LPI objects (lpi_xa entries, vgic_irq structures) from guest MMIO crosses the KVM virtual-machine security boundary and can enable guest-to-host escape.\nC:H - The race can erase a newly registered LPI while freeing the old one, leaving stale host pointers (ite->irq, ap_list) to freed vgic_irq objects; this use-after-free enables arbitrary host kernel memory disclosure via heap reuse.\nI:H - Host heap corruption of vgic_irq structures and xarray state from the race provides use-after-free write primitives that can be leveraged for arbitrary host kernel modification and VM escape code execution.\nA:H - The bug can cause host kernel crashes (oops/panic) from freeing live LPIs, orphaned IRQ state, and use-after-free during concurrent VGIC interrupt handling on multiple vCPUs."}]}],"providerMetadata":{"shortName":"Linux","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateUpdated":"2026-10-15T10:09:17.216Z"},"cpeApplicability":[{"nodes":[{"operator":"OR","negate":false,"cpeMatch":[{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.17","versionEndExcluding":"6.18.57"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.17","versionEndExcluding":"7.1.8"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.17","versionEndExcluding":"7.2"}]}]}],"x_generator":{"engine":"bippy-1.2.0"}},"adp":[{"title":"CISA ADP Vulnrichment","metrics":[{"other":{"type":"ssvc","content":{"id":"CVE-2026-74568","role":"CISA Coordinator","options":[{"Exploitation":"none"},{"Automatable":"no"},{"Technical Impact":"total"}],"version":"2.0.3","timestamp":"2026-10-16T19:00:27.387340Z"}}}],"providerMetadata":{"shortName":"CISA-ADP","orgId":"134c704f-9b21-4f2e-91b3-4a467353bcc0","dateUpdated":"2026-10-16T19:05:56.525Z"}}]}}}