Powody wyjścia VM

https://chacker.pl/

VM-Exit mogą być synchroniczne lub asynchroniczne, przy czym te drugie pochodzą ze źródeł, takich jak przerwania zewnętrzne lub timer wywłaszczania VMX. Synchroniczne wyjścia VM są spowodowane zachowaniem VM i mogą być warunkowe lub bezwarunkowe. Tylko bardzo niewiele instrukcji powoduje bezwarunkowe wyjścia VM (CPUID jest jednym z nich), a pozostałe spowodują warunkowe wyjście VM w zależności od konfiguracji VMCS (pola kontrolne). Można by pomyśleć, że instrukcje wrażliwe na sterowanie zawsze będą powodować wyjścia VM, ale jak widzieliśmy, można przyznać maszynie wirtualnej dostęp do zasobów systemowych, więc nie zawsze jest to prawdą. Może się również zdarzyć sytuacja odwrotna: niegroźne instrukcje, takie jak instrukcja PAUSE spin-loop hint, mogą być ustawione tak, aby powodować VM-Exit (aby rozwiązać problem wywłaszczenia posiadacza blokady). Inne wrażliwe instrukcje mogą być obsługiwane przez VMM (warunkowe VMExit) lub bezpośrednio przez sprzęt wirtualizacji. Wreszcie, te nieuprzywilejowane, wrażliwe instrukcje, o których mówiliśmy wcześniej w tym rozdziale, mogą być teraz prawidłowo obsługiwane.

Pola informacji o wyjściu maszyny wirtualnej VMCS

https://chacker.pl/

W przypadku zdarzeń wyjścia maszyny wirtualnej kilka pól VMCS jest aktualizowanych informacjami o charakterze zdarzenia. Najważniejsze jest pole exit-reason, które koduje przyczynę VM-Exit. Inne pola uzupełniają je o informacje specyficzne dla zdarzenia; niektóre z nich to kwalifikacja wyjścia, długość instrukcji, informacje o instrukcji, informacje o przerwaniu, liniowy adres gościa i fizyczny adres gościa.

Pola sterowania VMCS

https://chacker.pl/

Zachowanie trybu VMX innego niż root można selektywnie kontrolować za pomocą zestawu pól sterowania VMCS. Między innymi umożliwiają one:

  • Kontrolę, które zdarzenia mogą powodować wyjście maszyny wirtualnej.
  • Kontrolę przejść VM-Exit i VM-Enter, w tym szczegółów dotyczących zapisywania stanu procesora. W przypadku VM-Enter możliwe jest syntezowanie przerwań i wyjątków („wstrzykiwanie zdarzeń”).
  • Konfigurowanie różnych innych funkcji wirtualizacji, takich jak EPT, wirtualizacja APIC, nieograniczony gość i cieniowanie VMCS.

• Udzielanie dostępu do zasobów systemowych (mapy bitowe IO i MSR). Umożliwia to tworzenie uprzywilejowanych maszyn wirtualnych z możliwościami dostępu sprzętowego.

Obszar „stanu gościa” VMCS

https://chacker.pl/

Ten obszar odpowiada polom VMCS, w których stan procesora wirtualnego jest zapisywany po wyjściu z maszyny wirtualnej, a te same pola są używane do ładowania stanu procesora wirtualnego po wejściu do maszyny wirtualnej. Jest to minimalny stan, który powinien być obsługiwany przez sprzęt, aby bezpiecznie przejść z trybów VMX i powinien obejmować co najmniej następujące elementy:

  • RIP, RSP, RFLAGS.
  • Rejestry sterujące, DR7.
  • Selektor, prawa dostępu, baza i limit rejestrów segmentów, LDTR i TR. Baza i limit GDTR i IDTR.

• MSR: IA32_EFER, IA32_SYSENTER_CS, IA32_SYSENTER_ESP i IA32_SYSENTER_EIP.

VMX

https://chacker.pl/

Firma Intel wprowadziła zestaw instrukcji Virtual Machine Extensions (VMX), dodając dwa nowe tryby wykonywania procesora: tryb operacyjny VMX root i tryb operacyjny VMX non-root.23 Podobnie jak w trybie nadzorcy, tryb VMX root to miejsce, w którym działa oprogramowanie VMM, podczas gdy maszyny wirtualne działają w trybie VMX non-root. Nie należy mylić trybów działania VMX z poziomami pierścienia; są one całkowicie niezwiązane. Co więcej, maszyna wirtualna w trybie VMX non-root może wykonywać kod na dowolnym poziomie pierścienia; w związku z tym jednym z ograniczeń rozwiązywanych przez rozszerzenia wirtualizacji sprzętowej jest kompresja pierścieniowa.

UWAGA: Możesz znaleźć osoby mówiące o „Ring -1” (ujemna jedynka). Tak naprawdę mają na myśli tryb główny VMX. Tryby działania VMX nie są powiązane z poziomami pierścienia, a odwoływanie się do „Ring -1” powoduje jedynie zamieszanie.

Przejście z trybu głównego do trybu nie-root nazywa się VM-Enter,24 natomiast przejście z trybu nie-root do trybu głównego znane jest jako VM-Exit. Możemy myśleć o tym ostatnim jako o czymś podobnym do mechanizmu pułapki niewidocznej dla oprogramowania opisanego w sprzętowym wirtualizatorze Goldberga. Dzięki temu nowemu mechanizmowi pułapki wprowadzonemu przez tryby działania VMX, VMM nie musi już używać IDT do celów wirtualizacji, ale nadal musi go używać do obsługi wyjątków sprzętowych i przerwań. Do struktury danych znanej jako struktura sterowania maszyną wirtualną (VMCS) można uzyskać dostęp z trybu głównego VMX. Manipulując tą strukturą, VMM może kontrolować wiele aspektów wirtualizacji, w tym zachowanie przejść między trybami VMX. Zwykle VMCS jest przypisany do każdego wirtualnego procesora każdej maszyny wirtualnej (maszyna wirtualna może mieć więcej niż jeden wirtualny procesor), ale każdy fizyczny procesor (lub logiczny procesor, biorąc pod uwagę SMT) może mieć tylko jeden „bieżący” VMCS. Pola VMCS można sklasyfikować w kilku różnych grupach, które omówimy.

Wirtualizacja wspomagana sprzętowo

https://chacker.pl/

Do tej pory widzieliśmy, jak wirtualizację osiągnięto w architekturze x86 przed pojawieniem się wirtualizacji wspomaganej sprzętowo. Aby przezwyciężyć wcześniejsze ograniczenia architektoniczne i pomóc w rozwoju VMM, wprowadzono rozszerzenia sprzętowe. Około 2005 r. niezależnie opracowano dwie główne implementacje: Intel Virtualization Technology Extensions (VT-x) i AMD Virtualization (AMD-V), wcześniej znane jako Secure Virtual Machine (SVM). Pozostała część tego rozdziału obejmie niektóre aspekty VT-x.

Parawirtualizacja

https://chacker.pl/

Widzieliśmy, że prawidłowa wirtualizacja architektury x86 jest skomplikowana i w niektórych przypadkach powolna. Mając na celu uproszczenie projektu hiperwizora i poprawę jego wydajności, niektóre implementacje obrały inny kierunek. Zamiast symulować prawdziwy sprzęt, zapewniono syntetyczne interfejsy do komunikacji i współpracy między maszynami wirtualnymi a VMM. Aby korzystać z tych alternatywnych interfejsów, wymagane są modyfikacje systemu operacyjnego gościa. Jednym ze sposobów, w jaki goście parawirtualizowani mogą komunikować się z VMM, są hiperwywołania. Jest to analogiczne do koncepcji wywołań systemowych używanych przez programy użytkownika do żądania usług z jądra systemu operacyjnego, ale w tym przypadku to gość żąda usług od VMM. Hiperwywołania zastępują funkcjonalność zwykle oferowaną przez komponenty sprzętowe, takie jak procesor, MMU,21 sprzętowych timerów i kontroler przerwań, ale mogą również rozszerzać funkcjonalność o powiadomienia między maszynami wirtualnymi i obsługę pamięci współdzielonej. Urządzenia parawirtualizowane mogą zastąpić emulowaną kartę sieciową i urządzenia pamięci masowej, zastępując je modelem podzielonego sterownika. Urządzenia back-endowe działają w hoście (lub w uprzywilejowanej maszynie wirtualnej), a ich zadaniem jest zarządzanie zasobami systemowymi, oferując jednocześnie gościom syntetyczny interfejs (prostszy i szybszy niż emulowany interfejs sprzętowy). Sterowniki front-endowe działające w gościu komunikują się z urządzeniami back-endowymi. Podstawowa warstwa transportowa dla tego modelu może być zbudowana na wierzchu udogodnień komunikacji między maszynami wirtualnymi, zwykle opartych na buforach pierścieniowych nad pamięcią współdzieloną. Wiele aspektów parawirtualizacji pozostaje, nawet po tym, jak wirtualizacja wspomagana sprzętowo pokonała wiele ograniczeń, które ją spowodowały. Ponadto koncepcja hiperwywołania została włączona do sprzętu (VMCALL22). Obecnie większość hiperwizorów oferuje różne stopnie możliwości parawirtualizacji.

Stronicowanie w tle

https://chacker.pl/

Jednostka zarządzania pamięcią x86 (MMU) mapuje adresy wirtualne (VA) na adresy fizyczne (PA) za pomocą widocznej dla oprogramowania, drzewiastej struktury danych znanej jako wielopoziomowe tabele stron. W normalnych okolicznościach jądro systemu operacyjnego stale uzyskuje dostęp do tabel stron; jednak w wirtualizacji stanowią one krytyczny zasób i bezpośredni dostęp z maszyn wirtualnych nie może być dozwolony. Aby wirtualizować MMU, adresy fizyczne maszyn (czyli adresy fizyczne systemów, czyli SPA) muszą być niewidoczne dla gości, którzy zamiast tego powinni być prezentowani za pomocą zestawu pseudo-fizycznych adresów (czyli adresów fizycznych gości, czyli GPA). MMU można postrzegać jako „mapę ϕ”; zwirtualizowany MMU mapowałby GVA na GPA. Do mapowania GPA na SPA potrzebna jest „mapa f”, ale początkowo x86 nie posiadał takiego mechanizmu (został on później wprowadzony wraz z EPT). Aby obejść to ograniczenie, zaimplementowano techniki stronicowania w tle. Stronicowanie w tle polega na przejęciu „mapy ϕ” (tabel stron używanych przez MMU) i przedstawieniu gościom zestawu wirtualnych tabel stron(mapowanie GVA na GPA). Każda próba zapisu gościa do zestawu wirtualnych tabel stron zostanie zatrzymana w VMM, który odpowiednio synchronizuje rzeczywisty zestaw tabel stron (mapowanie GVA i HVA na SPA). Translacja adresów jest wykonywana poprzez przechodzenie przez wielopoziomowe tabele stron, co jest procesem całkowicie opartym na PA i rozpoczynającym się od PA najwyższej tabeli wskazywanej przez rejestr CR3. Natomiast po włączeniu stronicowania dostęp do pamięci spowodowany przez instrukcje jest oparty na VA, w tym na samych tabelach stron, dla których muszą być one mapowane samodzielnie, aby uzyskać do nich dostęp. Ta zasada samoodniesienia może być wykorzystana przez VMM do wdrożenia stronicowania w tle.

UWAGA Będziemy używać terminu system physical address (SPA) w odniesieniu do adresu fizycznego maszyny, a terminu guest physical address (GPA) w odniesieniu do pseudo-fizycznego adresu widzianego przez gościa. W przypadku numeru ramki strony (PFN) będziemy używać numeru ramki strony systemowej (SPFN) lub numeru ramki strony gościa (GPFN). W przypadku adresu wirtualnego (VA) będziemy używać adresu wirtualnego hosta (HVA) lub adresu wirtualnego gościa (GVA). Należy pamiętać, że istnieją inne schematy nazewnictwa odnoszące się do tych samych terminów, których używamy.

VMM musi obsługiwać próby dostępu gościa do konfiguracji MMU i/lub tabel stron. Aby uzyskać dostęp do konfiguracji MMU, gość musi wykonać uprzywilejowane instrukcje (aby uzyskać dostęp, na przykład, do rejestrów CR3 lub CR4). Pułapka na uprzywilejowane instrukcje, więc obsługa tego przypadku jest prosta. Obsługa dostępu gościa do tabel stron jest bardziej skomplikowana i obejmuje konstrukcję zestawu pseudo-tabel stron. Gdy gość próbuje ustawić PA na CR3, PA jest w rzeczywistości GPA dla tego, co gość uważa za najwyższy poziom własnych tabel stron. Pułapki dostępu do zapisu CR3 trafiają do VMM, który obsługuje je, wykonując następujące kroki:

  1. Pobierz SPA odpowiadające GPA użytemu przez gościa do ustawienia CR3.
  2. Zmapuj SPA na HVA i zacznij przeglądać pseudo-tabelę stron przez nią wskazywaną; każdy wpis zawiera GPA (GPFN) następnego poziomu tabel stron.
  3. Dla każdej tabeli stron GPA pobierz jej SPA i powtórz od kroku 2.
  4. Dla każdej tabeli stron utwórz tabelę stron cienia. Ta tabela stron cienia będzie używana przez MMU, ale będzie niewidoczna dla gościa. Wpisy będą albo wskazywać na tabelę następnego poziomu, albo mapować GVA. Jeśli wpis wskazuje na tabelę następnego poziomu, jej SPA musi wskazywać na odpowiadającą jej tabelę stron cienia. Jeśli mapuje GVA, to wpis musi kodować SPA GPA, który odpowiada GVA.
  5. Jeśli GVA mapuje GPA należące do zestawu pseudo-tabel stron, to odpowiadający mu wpis w tabeli stron cienia jest ustawiany jako tylko do odczytu.

W ten sposób za każdym razem, gdy gość próbuje zaktualizować własne tabele stron, próba zapisu zostanie przechwycona w VMM, który obsłuży dostęp i zaktualizuje odpowiadające im tabele stron cienia. Poniższa ilustracja pokazuje przykład, w jaki sposób GVA PDPT gościa może zostać przetłumaczony przez mechanizm stronicowania cienia.

Goście mogą czytać bezpośrednio ze swoich pseudo-tabel stron. Ponadto tłumaczenia MMU są wykonywane bezpośrednio z GPA do SPA przez tabele stron w tle, więc nie ma tam kosztów wydajnościowych. Z drugiej strony obsługa aktualizacji tabel stron jest złożona i kosztowna z dwóch powodów. Po pierwsze, specjalistyczne procedury interpretera (emulator x86) są wymagane dla każdej potencjalnej instrukcji wykonującej aktualizację. Po drugie, zarówno pseudo-tabele stron, jak i tabele stron w tle muszą być zsynchronizowane. Minimalnym kosztem każdej aktualizacji tabeli stron jest koszt pułapki w VMM (jak wyjaśniono wcześniej, koszt ten można zmniejszyć dzięki adaptacyjnemu tłumaczeniu binarnemu).

Kompresja pierścieniowa

https://chacker.pl/

Wiemy, że architektura wirtualizowalna wymaga dwóch trybów działania: trybu nadzorcy do uruchamiania VMM i trybu użytkownika do uruchamiania programów VM. Gdy x86 przejdzie w tryb chroniony, zapewnione są cztery poziomy ochrony (pierścienia), z których dwa są powszechnie używane przez systemy operacyjne: Ring-0 dla kodu jądra i Ring-3 dla programów użytkownika. Próby wykonania instrukcji uprzywilejowanej na dowolnym poziomie pierścienia innym niż Ring-0 powodują pułapkę (#GPF). Zgodnie z tym projektem maszyny wirtualne powinny być wykonywane tylko na poziomach pierścienia 1–3, podczas gdy VMM powinien działać na poziomie Ring-0. Aby to osiągnąć, jądro systemu operacyjnego działające na maszynie wirtualnej musi zostać zdegradowane z poziomu Ring-0 do jednego z pozostałych poziomów. Mechanizm stronicowania x86 może rozróżniać tylko strony „supervisor” i „user”. Poziomy pierścienia 0–2 mogą uzyskać dostęp do obu (z wyjątkiem sytuacji, gdy wymuszane są SMAP i SMEP15), podczas gdy Ring-3 może uzyskać dostęp tylko do stron „user”. Tak więc, jeśli chcemy użyć stronicowania do ochrony pamięci VMM, maszyny wirtualne powinny mieć możliwość działania tylko w trybie Ring-3. Oznacza to, że nie byłoby różnicy w poziomie uprawnień jądra i jego procesów użytkownika. W typowej implementacji systemu operacyjnego wirtualna przestrzeń adresowa jest podzielona na dwie połowy, mapując pamięć jądra na każdy proces użytkownika. Ten schemat ma kilka zalet, takich jak proste rozróżnianie wskaźników użytkownika/jądra, przechowywanie adresów jądra w pamięci podręcznej w TLB i możliwość wykonywania bezpośrednich operacji kopiowania z jądra na adresy użytkownika. Z drugiej strony, takie współdzielenie przestrzeni adresowej ułatwiało eksploatację jądra przez długi czas i stworzyło potrzebę wielu środków zaradczych (KASLR,16 UDEREF,17 SMEP i SMAP).

UWAGA W ostatnich latach odkryto szereg luk w zabezpieczeniach związanych z wykonywaniem przejściowym, które mogą być wykorzystane między innymi do wycieku zawartości pamięci uprzywilejowanej do trybu użytkownika. Aby złagodzić wiele z nich, wymuszana jest izolacja tabeli stron jądra (KPTI19); co zabawne, działa ona poprzez usuwanie większości mapowań jądra z procesów użytkownika, co niweczy większość korzyści wydajnościowych zapewnianych przez podział pamięci.

Współdzielenie przestrzeni adresowej staje się problematyczne, gdy jądro zostanie umieszczone w Ring-3. Tak jak jest, VMM nie może chronić pamięci jądra dla procesów użytkownika, chyba że zostanie ona odmapowana i ponownie zamapowana między przełączeniami kontekstowymi, ale byłoby to zbyt kosztowne. Rozwiązaniem jest umieszczenie jądra w Ring-1. W ten sposób stronicowanie może chronić pamięć jądra (strony nadzorcy) przed przestrzenią użytkownika. Jest pewien haczyk: stronicowanie nie może chronić stron nadzorcy VMM przed Ring-1, jednak nadal możemy użyć segmentacji, aby chronić pamięć VMM przed jądrem gościa.

UWAGA: x86_64 usunął większość funkcji segmentacji, więc rozwiązanie Ring-1 nie może być używane w trybie długim. Niektóre modele częściowo obsługują limity segmentów za pośrednictwem funkcji EFER.LMSLE20, ale obecnie częściej spotyka się procesory x86_64 z rozszerzeniami wirtualizacji sprzętowej, co, jak zobaczymy później, oszczędzi nam kłopotu związanego z martwieniem się o kompresję pierścieniową.

UWAGA: x86_64 usunął większość funkcji segmentacji, więc rozwiązanie Ring-1 nie może być używane w trybie długim. Niektóre modele częściowo obsługują limity segmentów za pośrednictwem funkcji EFER.LMSLE20, ale obecnie częściej spotyka się procesory x86_64 z rozszerzeniami wirtualizacji sprzętowej, co, jak zobaczymy później, oszczędzi nam kłopotu związanego z martwieniem się o kompresję pierścieniową.