Skip to content

IO bitmap interception (port I/O trapping)

Guest'in IN/OUT port I/O'sunu VMCS I/O bitmap'leri (0x2000/0x2002 field'leri) ile port bazında trap'leyip, hypervisor'ın inceleyebileceği ya da emule edebileceği bir I/O-instruction VM-exit (temel exit reason 30) üretmek.

Mechanism

Note

Bir VMX hypervisor'ı port I/O'yu iki temel processor-based VM-execution control'ü ile yönetir. Unconditional I/O exiting her IN/OUT'u exit'e çevirir. Use I/O bitmaps ise bunun yerine iki adet 4 KB bitmap'e bakar — 0x0000–0x7FFF portlarını kapsayan I/O bitmap A ve 0x8000–0xFFFF portlarını kapsayan I/O bitmap B. Set edilmiş bir bit "bu porta erişimde exit yap" anlamına gelir. Bunların guest-physical adresleri VMCS 64-bit control field'lerinde tutulur: 0x2000 (IO_BITMAP_A) ve 0x2002 (IO_BITMAP_B). Trap'lenmiş bir porta dokunulduğunda CPU, erişimi tarif eden bir exit qualification ile birlikte bir I/O instruction VM-exit, temel exit reason 30 teslim eder. Bu sayede bir monitor, belirli device portlarını izleyebilir (ya da bir virtual device'ı emule edebilir) ve trap'lenmemiş portların native çalışmasına izin verir.

Değişmez (invariant) kural: bitmap, instruction'ın etkisinden önce danışılır, yani host I/O denemesini tam operand context'iyle görür ve onu allow, deny, modify ya da emule edebilir.

Walkthrough

VMCS kurulumu ve handler akışı (Intel SDM Vol. 3C, "I/O bitmaps" / "Exit qualification for I/O instructions"; OSDev VMX notları):

  1. Control'ü seç. Primary processor-based control'lerde use I/O bitmaps'i set et (ve unconditional I/O exiting'i clear bırak ki yalnızca bitmap'lenmiş portlar trap'lensin). Program'la:
VMCS 0x2000  IO_BITMAP_A_ADDRESS = phys(bitmapA)   // ports 0x0000-0x7FFF
VMCS 0x2002  IO_BITMAP_B_ADDRESS = phys(bitmapB)   // ports 0x8000-0xFFFF
  1. Portları işaretle. Örneğin legacy COM1 data port 0x3F8'i trap'lemek için onun bit'ini bitmap A'da set et:
port = 0x3F8 (< 0x8000 -> bitmap A)
bitmapA[port >> 3] |= 1 << (port & 7);
  1. Exit reason 30'u handle et. Exit qualification; yön (in/out), boyut (1/2/4 byte), string mı immediate mı, REP prefix ve port numarasını kodlar:
case EXIT_REASON_IO_INSTRUCTION: { // 30
    uint64_t q = vmread(EXIT_QUALIFICATION);
    unsigned size  =  (q & 0x7) + 1;      // bits 0-2: access size - 1
    bool     is_in =  (q >> 3) & 1;       // bit 3: 1 = IN, 0 = OUT
    bool     str   =  (q >> 4) & 1;       // bit 4: string instruction
    bool     rep   =  (q >> 5) & 1;       // bit 5: REP prefixed
    uint16_t port  =  (q >> 16) & 0xFFFF; // bits 16-31: port number
    // inspect / emulate, then advance guest RIP past the IN/OUT
}

!!! warning Bir bitmap exit'inde CPU I/O'yu senin yerine yapmaz — handler'ın erişimi emule etmesi (gerçek portu ya da bir virtual device modelini read/write etmesi) ve guest RIP'i instruction uzunluğu kadar ilerletmesi gerekir, aksi halde guest sonsuza dek re-fault eder.

Beklenen gözlem: yalnızca bitmap'lenmiş portlara yapılan erişimler exit reason 30 üretir; diğerleri exit olmadan native çalışır.

Detection

  • Trap'lenmiş portlar daha yavaş çalışır (her erişim başına bir VM-exit). Bir guest, trap'lendiğinden şüphelenilen bir porta karşı in/out süresini, trap'lenmediği bilinen bir portunkiyle kıyaslayarak farkı görebilir; ne var ki birçok gerçek platform zaten herhangi bir hypervisor altında legacy portları trap'ler.

Mitigation

  • Düşük overhead için minimum port set'ini trap'le; monitor'ün ihtiyaç duymadığı hot portları trap'lenmemiş bırak.
  • Modern device monitoring'in çoğu legacy PIO yerine MMIO + EPT violation'ları kullanır, dolayısıyla I/O-bitmap trapping çoğunlukla legacy/virtual device'lar içindir.

References