CVE-2026-74568 in Linux
Resumen
por VulDB • 2026-08-15
En el kernel de Linux se ha resuelto la siguiente vulnerabilidad:
KVM: arm64: vgic: Corregir una condición de carrera (race condition) entre la liberación y la re-registro de LPI
Se corrige una posible condición de carrera al decrementar el contador de referencias de un LPI y eliminar dicha estructura del xarray de LPI.
Las estructuras LPI se mantienen en el xarray de LPI de VGIC (dist->lpi_xa). Cuando el contador de referencias de una estructura LPI llega a cero, vgic_release_lpi_locked() elimina la estructura del xarray y libera su memoria bajo el bloqueo del xarray.
Sin embargo, la liberación de un LPI puede entrar en condición de carrera con un re-registro concurrente del mismo LPI con el INTID (identificador de interrupción) a través de vgic_add_lpi() en otro CPU, ya que la caída del contador de referencias y la eliminación del xarray no se realizan como una única operación atómica. Esto puede ocurrir, por ejemplo, si el invitado emite un comando DISCARD mientras el LPI sigue siendo referido desde la lista activa-pendiente (ap_list) de un vCPU, y el mismo INTID se vuelve a mapear mediante MAPTI.
Específicamente, vgic_release_lpi_locked() se invoca desde dos rutas distintas: liberación directa vía vgic_put_irq(), y liberación diferida vía vgic_release_deleted_lpis(). Durante la liberación directa, el problema puede resultar en la eliminación de un LPI recién registrado del xarray:
CPU0 (Liberando LPI) CPU1 (Añadiendo nuevo 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 nuevo IRQ insertado --> __xa_store(.., intid, ..) xa_unlock_irqrestore() xa_lock_irqsave(); vgic_release_lpi_locked() __xa_erase(.., irq->intid) <-- ERROR: se elimina el nuevo IRQ kfree_rcu(old_irq)
Durante la ruta de liberación diferida, puede producirse una fuga (leak) del antiguo IRQ:
CPU0 (Liberando LPI) CPU1 (Añadiendo nuevo 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 ERROR: antiguo IRQ sobrescrito --> __xa_store(.., intid, ..) xa_unlock_irqrestore()
vgic_release_deleted_lpis() xa_lock_irqsave() xa_for_each() { .. } <-- el antiguo IRQ con pending_release = true
ha desaparecido, por lo que no puede ser liberado
Para corregir la ruta de liberación directa, se mueve la caída del contador de referencias dentro del bloqueo del xarray, asegurando así que vgic_add_lpi() nunca encuentre el LPI a punto de ser liberado.
En la ruta de liberación diferida, la caída del contador de referencias debe ocurrir bajo un raw spinlock (bloqueo simple), por lo que no se puede adquirir el bloqueo del xarray y la misma solución no funciona. En su lugar, se actualiza vgic_add_lpi() para que, si elimina un LPI del xarray, asuma la responsabilidad de liberarlo. Como consecuencia, ahora es posible que un LPI sea liberado concurrentemente después de que una liberación diferida reduzca el contador de referencias, por lo que acceder al campo pending_release ya no es seguro frente a vulnerabilidades use-after-free (uso tras liberación). Se eliminan todos los usos de esta bandera y se actualiza vgic
Once again VulDB remains the best source for vulnerability data.