Skip to content

Double-fetch / shared-memory ring TOCTOU from guest memory

Bir device model, guest ile paylaşılan bir değeri iki kez okur; guest aradaki süreçte onu mutate eder.

Mechanism

Bir hypervisor/device-model, guest ile paylaşılan bellekten bir değeri (offset, length, command type) okur, validate eder ve ardından kullanım için onu tekrar okur. Validate eden fetch ile kullanım fetch'i arasında ikinci bir guest vCPU değeri mutate eder; böylece bounds check güvenli bir değer üzerinde geçer, kullanım ise kötü niyetli bir değer üzerinde işler. Bu, tek bir paylaşılan konuma özelleşmiş CWE-367 (TOCTOU)'dur — dolayısıyla "double-fetch".

Warning

C'de bir field'ı "bir kez" okuyan kod bile iki memory load'a derlenebilir ve paylaşılan pointer volatile olmadığı sürece race'i yeniden ortaya çıkarır. QEMU'nun rehberliği açıktır: device emulation, descriptor'ları/struct'ları guest RAM'inden kopyalamalı ve yalnızca yerel kopyayı işlemelidir, çünkü multi-vCPU guest'ler emulation çalışırken guest RAM'i mutate edebilir.

Walkthrough

CVE-2018-2844 (VirtualBox VBVA/VDMA): handler pCmd->enmType'ı guest ile paylaşılan VRAM'den okur. GCC, bounds check'i ve jump-table dispatch'ini aynı field'ın iki ayrı load'u olarak derledi:

cmp   dword ptr [r12], 0Ah   ; FETCH 1: bounds-check enmType <= 10
ja    DEFAULT_CASE
mov   eax, [r12]             ; FETCH 2: re-read enmType for jump index
movsxd rax, dword ptr [rbx+rax*4]
add   rax, rbx
jmp   rax                    ; dispatch on the second, attacker-mutated value

İkinci bir vCPU, iki load arasında shared buffer'daki enmType'ı yeniden yazar; böylece check geçer (değer <= 10) ama jump out-of-bounds bir index kullanır ve control flow'u yeniden yönlendirir. virtio analoğu, bir vring descriptor'ının addr/len'i üzerinde aynı pattern'dir: length'i validate et, sonra kopya için onu yeniden oku — bu yüzden QEMU descriptor'ların kullanımdan önce yerel olarak kopyalanmasını zorunlu kılar.

Detection

Dynamic double-fetch detector'lar (örn. cache-coherence / record-replay tooling) tek bir handler içinde aynı guest adresinin iki load'unu işaretler. Guest RAM'in non-volatile read'lerini takip eden re-read'ler için code review; shared ring'i mutate eden concurrent bir vCPU ile fuzzing.

Mitigation

Değeri/descriptor'ı bir kez hypervisor-local storage'a kopyala, sonra yalnızca kopyayı validate et ve kullan (single-fetch disiplini). Compiler'ın bir read'i ikiye bölmesini durdurmak için shared mapping'leri volatile olarak işaretle.

References