Skip to content

Linux KVM SEV-ES VMGEXIT string-IO OOB (CVE-2021-4093)

CVE-2021-4093: bir SEV-ES guest, count/size'ı tek sayfalık pio_data buffer'ını aşan bir string I/O instruction'ı (ins/outs, exit reason SVM_EXIT_IOIO) için bir VMGEXIT oluşturabilir ve L0 host kernel'inde bir out-of-bounds read/write sürebilir.

Mechanism

Bug class: guest-controlled uzunluk vs. sabit boyutlu bir host I/O buffer'ı

Risk altındaki boundary encrypted SEV-ES guest -> L0 host kernel'dir. SEV-ES ile guest register state'i encrypted'tır, dolayısıyla guest emulated-instruction verisini host'a açıkça GHCB üzerinden bir VMGEXIT ile vermek zorundadır. String port I/O için, KVM veriyi guest GHCB buffer'ı ile per-vCPU vcpu->arch.pio_data buffer'ı arasında kopyalar.

Kırılan invariant: pio_data bir sayfadır, ama string-I/O emulation'ı transfer size'ını guest-controlled repeat count ve access size'tan, o sayfaya karşı bound'lamadan türetiyordu. Aşırı boyutlu bir count advertise eden malicious bir VMGEXIT bu yüzden host memcpy'ının buffer'ı aşmasına neden olur — host tarafı bir out-of-bounds-write (ve read). Sıradan I/O emulation'ının aksine, SEV-ES bunu tamamen guest-supplied GHCB üzerinden yönlendirir, yani parametreleri tamamen guest kontrol eder.

Corruption pio_data'ya komşu kernel allocation'larına düştüğünden, dökümante edilmiş en kötü durum host crash'inden potansiyel bir guest-to-host escape'e doğru tırmanır. Bkz. severity-code-injection-attacks-against-encrypted-vms.

Walkthrough

Public Red Hat Bugzilla'sından ve upstream SEV-ES string-I/O fix serisinden çıkarılan yüksek seviyeli reprodüksiyon şekli (AMD host, SEV-ES guest):

  1. Bir SEV-ES guest, bir string I/O instruction'ı (ins/outs) çalıştırır ve SVM_EXIT_IOIO üzerinden exit eder, operasyonu bir VMGEXIT'te GHCB üzerinden sağlar.
  2. Guest, bir sayfadan büyük bir repeat count / access size advertise eder.
  3. Host string-I/O handler'ı (kvm_sev_es_string_io / sev_es_string_io), o kadar byte'ı tek sayfalık pio_data buffer'ına/buffer'ından kopyalar.
  4. Bound'lanmamış memcpy, pio_data'nın ötesini okur/yazar ve komşu host kernel memory'sini corrupt eder.

Fix her transfer'ı buffer'a bound'lar ve GHCB verisi daha büyük olduğunda, onu tek bir aşırı boyutlu kopya yerine birden çok sayfa boyutlu pass'te işler.

Fix'in temsili şekli (arch/x86/kvm/svm/sev.c)
/* Never copy more than the pio_data page at once; iterate in passes
 * sized to the buffer instead of trusting the guest-supplied count. */
while (count) {
        unsigned int n = min(count, PIO_DATA_CAPACITY);
        /* ... copy n elements, advance, repeat ... */
        count -= n;
}

Örnekleyici fragment

Multi-pass fix niyetinin kavramsal temsili, birebir kopya değil. Tam kod için atıf yapılan Red Hat Bugzilla / upstream commit'lerine bak.

Detection

  • SEV-ES guest'leri çalıştıran AMD host'ları: backtrace'i SVM string-I/O / SVM_EXIT_IOIO handling'inden geçen KASAN out-of-bounds splat'ları ya da host oops'ları için dikkatli ol.
  • Çok büyük repeat count'lu anormal VMGEXIT string-I/O istekleri tetikleyici sinyaldir; mümkün olduğu yerde GHCB-driven I/O size'larını instrument et/sınırla.
  • Port I/O tetikleyen spesifik bir confidential-computing tenant'ıyla ilintili crash'ler.
  • Bu OOB class'ını exploitation öncesi yüzeye çıkarmak için debug/canary host'larını KASAN ile çalıştır.

Mitigation

  • SEV-ES string-I/O hardening serisinde upstream düzeltildi (Linux 5.15; 5.14.16'ya backport edildi). Dağıtımının patch'li kernel'ini uygula (Red Hat Bugzilla 2028584 fix'leri izler; Ubuntu update'ler shipledi).
  • Patch'lenene kadar hangi tenant'ların SEV-ES guest çalıştırabileceğini kısıtla; surface yalnızca SEV-ES VM'leri için var.
  • /dev/kvm erişimini trusted workload'larla sınırlı tut.

References