QEMU LSI53C895A SCSI infinite loop (CVE-2019-12068)¶
Hazırlanmış bir SCSI script'i, emüle LSI53C895A controller'ını sonlanmayan bir execution loop'una sürebilir, bir host CPU'sunu meşgul edip QEMU'yu askıya alabilir (guest-to-host DoS).
Mechanism¶
Bug sınıfı: sınırsız guest-driven interpreter loop'u (ilerleme/sonlanma sınırı yok)
LSI53C895A, scriptable bir SCSI controller'dır: guest memory'ye küçük bir "SCRIPTS"
programı yerleştirir ve device onu çalıştırır. QEMU bunu lsi_execute_script()'te
emüle eder; bu, script pointer'ı s->dsp'deki opcode'u fetch eden, üzerinde işlem
yapan ve s->dsp'yi bir sonraki instruction'a ilerleten bir döngüdür.
Interpreter'ın zorlaması gereken invariant, invocation başına sınırlı toplam maliyetle ileri ilerlemedir. Orijinal döngü yalnızca belirli instruction sonuçlarında sonlanırdı; no-op / boş opcode'lar döngüyü güvenilir şekilde kırmadı ya da herhangi bir limite doğru sayılmadı. Bir guest, interpreter'ın ne anlamlı ilerleme kaydeden ne de bir çıkış koşuluna ulaşan opcode'ları fetch etmeye devam edeceği bir script hazırlayabilir (ya da memory ayarlayabilir).
Tüm döngü QEMU vCPU/IO thread'i içinde senkron çalıştığı için, sonlanmayan bir script kontrolü asla bırakmaz: bir host CPU'su %100'de döner ve VM (ve o QEMU thread'i) yanıt vermez hale gelir. Sınır aşılır çünkü guest, host'un körü körüne yorumladığı programı tamamen yazar ve host meşru bir script'in eninde sonunda sonlandığına güvendi.
Walkthrough¶
Upstream patch serisine dayalı kavramsal reproduction.
-
Guest, LSI controller'ı, instruction stream'i sonlandırıcı bir opcode'a yakınsamayan bir SCRIPTS programını çalıştıracak şekilde programlar (örn. eski döngünün ne sınırladığı ne de üzerinde çıktığı bir no-op/boş opcode dizisi).
-
Guest, script execution'ı başlatır; QEMU
lsi_execute_script()'e girer.
Conceptual loop shape (logical fragments, not an exploit)
- Bir çıkış koşuluna asla ulaşmayan bir opcode stream'iyle döngü süresiz çalışır; host thread'i takılır ve VM askıya alınır.
Corruption değil, DoS
Bu saf bir availability bug'ıdır: burada bir memory write primitive'i yoktur. Hasar, sıkışmış bir host thread'i / CPU tükenmesidir; bu, paylaşılan host'larda co-tenant VM'leri bozabilir.
Fix, interpreter'ı sınırlar: bir instruction count limit'i (LSI_MAX_INSN, 10000)
tanıtır ve o kadar instruction — no-op'lar dahil — çalıştırıldığında döngüden çıkar,
bir illegal-instruction interrupt'ı (LSI_DSTAT_IID) yükseltir, disconnect eder ve
script'i durdurur:
/* Conceptual shape of the upstream fix (paraphrased) */
if (insn_processed++ > LSI_MAX_INSN) { /* LSI_MAX_INSN == 10000 */
/* treat as illegal: raise DSTAT.IID, disconnect, stop running */
lsi_script_dma_interrupt(s, LSI_DSTAT_IID);
break;
}
Detection¶
Host ve telemetry sinyalleri
- CPU pinning: I/O ilerlemesi olmadan ~%100 CPU'da takılı bir QEMU thread'i;
host'taki
perf top/ bir profiler, zamanınlsi_execute_scriptve dispatch helper'larında yoğunlaştığını gösterir. - Guest hang: QEMU process'i canlı kalırken etkilenen VM yanıt vermeyi durdurur (bunu crash tarzı DoS CVE'lerinden ayırır).
- Anomaly: büyük/döngüsel LSI SCRIPTS aktivitesi ya da idle olması gereken device'lardan LSI DMA çıkaran bir guest şüphelidir.
- Watchdog: tek bir tenant'la ilişkilenen host seviyesindeki "VM yanıt vermiyor ama process çalışıyor" uyarıları, guest-driven bir spin loop'una işaret eder.
Mitigation¶
Remediation
- Patch: "scsi: lsi: exit infinite loop while executing script (CVE-2019-12068)"
fix'ine (
LSI_MAX_INSNcap) sahip bir QEMU build'ine ya da bir distro backport'una (Oracle/EulerOS ve diğerleri gönderdi) yükseltin. - Reduce attack surface: legacy
lsi53c895acontroller'ı yerinevirtio-scsi/virtio-blk'i tercih edin; LSI modelini ihtiyacı olmayan guest'lere expose etmeyin. - Resource isolation: QEMU CPU'sunu cgroup'larla (
cpu.max) pin'leyin/sınırlayın ki dönen bir thread co-located workload'ları aç bırakamasın; hung-VM koşulları için denetleyin ve restart edin. - Confinement: etkiyi sorunlu instance'a sınırlamak için standart
svirt/seccomp sertleştirmesi.