GHSA-GXV2-5VPW-RGJQ

Vulnerability from github – Published: 2026-08-15 15:30 – Updated: 2026-08-17 06:33
VLAI
Details

In the Linux kernel, the following vulnerability has been resolved:

KVM: arm64: vgic: Fix race between LPI release and re-registration

Fix a potential race between decrementing an LPI's reference count and evicting that structure from the LPI xarray.

LPI structures are maintained in the VGIC LPI xarray (dist->lpi_xa). When the reference count of an LPI structure drops to zero, vgic_release_lpi_locked() removes the structure from the xarray and frees it under the xarray lock.

However, the release of an LPI can race with a concurrent LPI re-registration with the same INTID via vgic_add_lpi() on another CPU, since the reference count drop and the xarray eviction are not performed in a single atomic step. This can happen e.g. if the guest issues a DISCARD while the LPI is still referenced from a vCPU's active-pending list (ap_list), and the same INTID is re-mapped via MAPTI.

Particularly, vgic_release_lpi_locked() is called from two distinct paths: direct release via vgic_put_irq(), and deferred release via vgic_release_deleted_lpis(). During direct release, the issue can result in deleting a newly registered LPI from the xarray:

CPU0 (Releasing LPI) CPU1 (Adding new LPI) ==================== ===================== vgic_put_irq() __vgic_put_irq() refcount_dec_and_test() vgic_add_lpi() xa_lock_irqsave() old_irq = xa_load(.., intid) vgic_try_get_irq_ref(old_irq) == false new IRQ inserted --> __xa_store(.., intid, ..) xa_unlock_irqrestore() xa_lock_irqsave(); vgic_release_lpi_locked() __xa_erase(.., irq->intid) <-- BUG: new IRQ is erased kfree_rcu(old_irq)

During the deferred release path, the old IRQ can be leaked:

CPU0 (Releasing LPI) CPU1 (Adding new LPI) ==================== ===================== vgic_put_irq_norelease() __vgic_put_irq() refcount_dec_and_test() irq->pending_release = true vgic_add_lpi() xa_lock_irqsave() old_irq = xa_load(.., intid) vgic_try_get_irq_ref(oldirq) == false BUG: old IRQ overwritten --> __xa_store(.., intid, ..) xa_unlock_irqrestore()

vgic_release_deleted_lpis() xa_lock_irqsave() xa_for_each() { .. } <-- old IRQ with pending_release = true is gone, so it cannot be released

To fix the direct release path, move the reference count drop inside the xarray lock, making sure that vgic_add_lpi() never encounters the to-be-released LPI.

In the deferred release path, the refcount drop must happen under a raw spinlock, so the xarray lock cannot be grabbed, and the same solution does not work. Instead, update vgic_add_lpi(), so that if it evicts an LPI from the xarray, it takes on the responsibility of freeing it. Consequently, an LPI may now be freed concurrently after a deferred release drops the refcount, so accessing the pending_release field is no longer safe from use-after-free. Delete all uses of the flag, and update vgic_release_deleted_lpis() to identify orphaned LPIs purely based on their refcount.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-74568"
  ],
  "database_specific": {
    "cwe_ids": [],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-08-15T13:18:02Z",
    "severity": "CRITICAL"
  },
  "details": "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\u0027s reference count and\nevicting that structure from the LPI xarray.\n\nLPI structures are maintained in the VGIC LPI xarray (dist-\u003elpi_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\u0027s 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 --\u003e  __xa_store(.., intid, ..)\n                                              xa_unlock_irqrestore()\n  xa_lock_irqsave();\n  vgic_release_lpi_locked()\n      __xa_erase(.., irq-\u003eintid)   \u003c-- 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-\u003epending_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 --\u003e __xa_store(.., intid, ..)\n                                              xa_unlock_irqrestore()\n\n  vgic_release_deleted_lpis()\n      xa_lock_irqsave()\n      xa_for_each() { .. } \u003c-- 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.",
  "id": "GHSA-gxv2-5vpw-rgjq",
  "modified": "2026-08-17T06:33:54Z",
  "published": "2026-08-15T15:30:36Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-74568"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/292e80a159aa88635bf668a7212cfdf526b8bd52"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/cbfe2b24a1ea9de35032dbdd100fdc700f5be92d"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…