
QEMU 11.1.2 - Élevé - Local - UAF - NVMe SGL span into BAR0 incomplete CVE-2021-3929
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · qemu-nvme-sgl-bar
QEMU v11.1.2 (4fc49f46dc95d4a27de2509e7fceb2931e91faeb) - NVMe
Classe : UAF (liste de refus BAR0 incomplète ; SGL couvrant le DMA dans le MMIO). Portée : Locale (invité).
nvme_map_addr est le reliquat de CVE-2021-3929. La vérification iomem ne porte que sur le début. Un bloc de données SGL qui commence juste avant BAR0 et s'étend dans MBAR est accepté. Identify rapporte toujours NVME_CTRL_SGLS_SUPPORT_NO_ALIGN. PRP ne peut pas couvrir un BAR aligné sur une page. SGL le peut.
Une lecture NVMe avec ce SGL passe par nvme_blk_read -> dma_blk_read. Le DMA rebond effectue ensuite des address_space_write des octets du disque invité sur BAR0 depuis le callback du helper DMA, en dehors du BH mem_reentrancy_guard de NVMe. Des zéros choisis par l'invité atterrissent sur CC. nvme_ctrl_reset s'exécute alors que la NvmeRequest active est encore dans le chemin d'achèvement.
Un pilote NVMe invité peut couvrir un SGL dans BAR0, réinitialiser le contrôleur depuis le démontage DMA et entrer en course avec la requête active.
| ID | pas encore de CVE |
| CWE | CWE-119 / CWE-416 |
| CVSS | Élevé : 8.2 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H |
| Produit | QEMU |
| Affecté | v11.1.2 (4fc49f46) |
| Auth | noyau invité avec un contrôleur NVMe (SGL) |
| Licence | GNU Affero GPL v3.0 |
| Labo | 127.0.0.1:18720 qtest uniquement |
Qui : noyau invité non fiable avec un périphérique PCI NVMe. De l'invité vers l'hôte.
| Méthode | Cible | Privilège |
|---|---|---|
NVMe READ SGL addr=0xdfffffec len=0x1000 (commence 0x14 avant BAR0 à 0xe0000000) | nvme_map_addr / nvme_addr_is_iomem début uniquement | Mapping accepté. Control Identify avec début dans BAR0 renvoie toujours 0x4004 (DATA_TRAS_ERROR | DNR). |
Le démontage du rebond écrit des zéros LBA sur CC | address_space_write du rebond DMA dans BAR0, nvme_write_bar | CSTS.RDY chute. pci_nvme_mmio_stopped dans le journal. Réinitialisation du contrôleur depuis le helper DMA, pas depuis un store MMIO invité dans le BH NVMe. |
Octets de disque contrôlés par l'invité sur CC / d'autres registres BAR0. La réinitialisation annule les AIO en cours, mais la requête qui est à l'intérieur du démontage DMA/achèvement entre encore en course avec la NvmeRequest / SQ / CQ active. Même classe que CVE-2021-3929 ; la liste de refus n'a jamais couvert un SGL couvrant. Le rebond DIF (nvme_tx depuis un callback AIO) présente le même trou.
Le contrôleur s'arrête (CSTS.RDY=0, mmio-stopped=1). Le processus de l'hyperviseur reste actif dans ce labo (asan=0). Un UAF de NvmeRequest par réinitialisation imbriquée est l'étape suivante déduite, pas le témoin imprimé par cette exécution.
Aucune. BAR0 NVMe est visible par l'invité.
0x4004.nvme_post_cqes est protégé, le MMIO imbriqué est MEMTX_ACCESS_ERROR.Le labo ne démontre pas de contrôle du RIP ni de shell de l'invité vers l'hôte. La classe est le DMA couvrant dans le MMIO plus la réinitialisation du contrôleur d'une requête active.
CVE-2021-3929 a appris à l'arbre à rejeter iomem comme point de départ d'un mapping NVMe. J'ai demandé si la fin d'un bloc de données SGL était incluse dans ce test. Elle ne l'est pas. CMB/PMR vérifient déjà les deux extrémités. BAR0 ne le fait pas. Identify annonce un SGL non aligné.
Fausses pistes : PRP (borné par page, ne peut pas s'infiltrer dans un BAR aligné sur page depuis la page précédente) ; commandes admin (SGL rejeté) ; espérer qu'ASan se déclenche au premier READ (le témoin est CSTS.RDY et pci_nvme_mmio_stopped, pas un rapport de tas).
cd lab
./run.sh
Cibler uniquement 127.0.0.1:18720 (qtest). Le pin est cloné depuis github.com/qemu/QEMU tag v11.1.2 / 4fc49f46. Loopback. Witness-only. L'image disque est 1 MiB de zéros afin que les écritures de rebond CC.EN=0.
SUCCESS qemu-nvme-sgl-bar control-sc=0x4004 span-addr=0xdfffffec span-end=0xe0000fec csts-rdy-before=1 csts-rdy-after=0 mmio-stopped=1 asan=0 QEMU-NVME-SGL-BAR-WITNESS
Replay : python3 qemu-nvme-sgl-bar-Abraxas-Labs.py.
Rejeter un mapping si un octet quelconque de [addr, addr+len) se trouve dans BAR0 (et MSI-X), conformément à CMB/PMR. nvme_addr_is_iomem devrait prendre une plage, pas un point de départ. Conserver le rejet du départ-dans-BAR0 comme contrôle de CVE-2021-3929.
4fc49f46dc95d4a27de2509e7fceb2931e91faebnvme_map_addr / nvme_addr_is_iomemGNU Affero GPL v3.0. Voir LICENSE. Laboratoire en boucle locale uniquement. Aucune garantie.