機密計算虛擬機器

Hyper-V 可以建立並執行作為機密計算 (CoCo) 虛擬機器的 Linux 客戶機。此類虛擬機器與物理處理器協作,以更好地保護虛擬機器記憶體中資料的機密性和完整性,即使在管理程式/虛擬機器管理器 (VMM) 已被攻破並可能惡意行為的情況下也能實現。Hyper-V 上的 CoCo 虛擬機器與Linux x86 虛擬化中的機密計算中描述的通用 CoCo 虛擬機器威脅模型和安全目標一致。請注意,Linux 中特定於 Hyper-V 的程式碼將 CoCo 虛擬機器稱為“隔離虛擬機器”或“隔離式虛擬機器”。

Hyper-V 上的 Linux CoCo 虛擬機器需要以下元件的協作和互動

  • 帶有支援 CoCo 虛擬機器的處理器的物理硬體

  • 硬體執行支援 CoCo 虛擬機器的 Windows/Hyper-V 版本

  • 虛擬機器執行支援作為 CoCo 虛擬機器的 Linux 版本

物理硬體要求如下

  • 具有 SEV-SNP 的 AMD 處理器。Hyper-V 不執行使用 AMD SME、SEV 或 SEV-ES 加密的客戶機虛擬機器,並且此類加密不足以滿足 Hyper-V 上的 CoCo 虛擬機器要求。

  • 具有 TDX 的 Intel 處理器

要建立 CoCo 虛擬機器,在建立虛擬機器時必須向 Hyper-V 指定“隔離虛擬機器”屬性。虛擬機器建立後,不能從 CoCo 虛擬機器更改為普通虛擬機器,反之亦然。

操作模式

Hyper-V CoCo 虛擬機器可以執行在兩種模式下。模式在建立虛擬機器時選擇,並且在虛擬機器的生命週期內不能更改。

  • 全面感知模式。在此模式下,客戶機作業系統經過“感知”以理解和管理作為 CoCo 虛擬機器執行的所有方面。

  • 半虛擬化管理程式模式。在此模式下,客戶機與主機之間的半虛擬化管理程式層提供作為 CoCo 虛擬機器執行所需的一些操作。客戶機作業系統可以比全面感知模式所需的 CoCo 感知度更少。

從概念上講,全面感知模式和半虛擬化管理程式模式可以視為一個光譜上的點,該光譜涵蓋了作為 CoCo 虛擬機器執行所需的客戶機感知程度。全面感知模式是光譜的一端。半虛擬化管理程式模式的完整實現是光譜的另一端,其中作為 CoCo 虛擬機器執行的所有方面都由半虛擬化管理程式處理,並且一個對記憶體加密或 CoCo 虛擬機器其他方面一無所知的普通客戶機作業系統可以成功執行。然而,Hyper-V 對半虛擬化管理程式模式的實現並未達到如此程度,它處於光譜的中間位置。CoCo 虛擬機器的一些方面由 Hyper-V 半虛擬化管理程式處理,而客戶機作業系統必須對其他方面進行“感知”。不幸的是,沒有對半虛擬化管理程式可能提供的特性/功能進行標準化的列舉,也沒有客戶機作業系統查詢半虛擬化管理程式以獲取其提供的特性/功能的標準化機制。對半虛擬化管理程式所提供內容的理解是硬編碼在客戶機作業系統中的。

半虛擬化管理程式模式與Coconut 專案有相似之處,該專案旨在提供一個受限的半虛擬化管理程式,為客戶機提供虛擬 TPM 等服務。然而,Hyper-V 半虛擬化管理程式通常處理比當前為 Coconut 設想的更多 CoCo 虛擬機器方面,因此更接近光譜中“無需客戶機感知”的一端。

在 CoCo 虛擬機器威脅模型中,半虛擬化管理程式位於客戶機安全域中,並且必須受到客戶機作業系統的信任。這意味著,管理程式/VMM 必須像保護自己免受潛在惡意客戶機攻擊一樣,保護自己免受潛在惡意半虛擬化管理程式攻擊。

全面感知模式與半虛擬化管理程式模式的硬體架構方法因底層處理器的不同而異。

  • 對於 AMD SEV-SNP 處理器,在全面感知模式下,客戶機作業系統在 VMPL 0 中執行,並完全控制客戶機上下文。在半虛擬化管理程式模式下,客戶機作業系統在 VMPL 2 中執行,半虛擬化管理程式在 VMPL 0 中執行。在 VMPL 0 中執行的半虛擬化管理程式擁有 VMPL 2 中客戶機作業系統所不具備的特權。某些操作需要客戶機呼叫半虛擬化管理程式。此外,在半虛擬化管理程式模式下,客戶機作業系統按照 SEV-SNP 架構的定義在“虛擬記憶體頂部”(vTOM) 模式下執行。當使用半虛擬化管理程式時,此模式簡化了客戶機對記憶體加密的管理。

  • 對於 Intel TDX 處理器,在全面感知模式下,客戶機作業系統在 L1 虛擬機器中執行。在半虛擬化管理程式模式下,使用 TD 分割槽。半虛擬化管理程式在 L1 虛擬機器中執行,而客戶機作業系統在巢狀的 L2 虛擬機器中執行。

Hyper-V 向客戶機公開了一個合成 MSR,用於描述 CoCo 模式。該 MSR 指示底層處理器是否使用 AMD SEV-SNP 或 Intel TDX,以及是否正在使用半虛擬化管理程式。構建一個可以在任一架構和任一模式下正確啟動和執行的單一核心映像是直截了當的。

半虛擬化管理程式影響

在半虛擬化管理程式模式下執行會影響通用 Linux 核心 CoCo 虛擬機器功能的以下方面

  • 客戶機初始記憶體設定。當在半虛擬化管理程式模式下建立新虛擬機器時,半虛擬化管理程式首先執行,並將客戶機物理記憶體設定為加密狀態。客戶機 Linux 執行正常的記憶體初始化,但會明確將適當的範圍標記為已解密(共享)。在半虛擬化管理程式模式下,Linux 不執行在全面感知模式下使用 AMD SEV-SNP 特別棘手的早期啟動記憶體設定步驟。

  • #VC/#VE 異常處理。在半虛擬化管理程式模式下,Hyper-V 配置客戶機 CoCo 虛擬機器將 #VC 和 #VE 異常分別路由到 VMPL 0 和 L1 虛擬機器,而不是客戶機 Linux。因此,這些異常處理程式不在客戶機 Linux 中執行,也不是半虛擬化管理程式模式下 Linux 客戶機所需的感知功能。

  • CPUID 標誌。AMD SEV-SNP 和 Intel TDX 都在客戶機中提供一個 CPUID 標誌,指示虛擬機器正在使用各自的硬體支援。雖然這些 CPUID 標誌在全面感知 CoCo 虛擬機器中可見,但半虛擬化管理程式會過濾掉這些標誌,客戶機 Linux 看不到它們。在整個 Linux 核心中,明確測試這些標誌的做法已基本被 cc_platform_has() 函式取代,目標是抽象 SEV-SNP 和 TDX 之間的差異。但 cc_platform_has() 抽象也允許 Hyper-V 半虛擬化管理程式配置選擇性地啟用 CoCo 虛擬機器功能的一些方面,即使 CPUID 標誌未設定。SEV-SNP 上的早期啟動記憶體設定是個例外,它會測試 CPUID SEV-SNP 標誌。但在 Hyper-V 半虛擬化管理程式模式虛擬機器中沒有該標誌,可以實現不執行 SEV-SNP 特定早期啟動記憶體設定的預期效果。

  • 裝置模擬。在半虛擬化管理程式模式下,Hyper-V 半虛擬化管理程式提供 IO-APIC 和 TPM 等裝置的模擬。由於模擬發生在客戶機上下文中的半虛擬化管理程式中(而不是管理程式/VMM 上下文),因此對這些裝置的 MMIO 訪問必須是加密引用,而不是在全面感知 CoCo 虛擬機器中使用的已解密引用。__ioremap_caller() 函式已得到增強,可以進行回撥以檢查特定地址範圍是否應被視為加密(私有)。請參閱“is_private_mmio”回撥。

  • 記憶體加密/解密轉換。在 CoCo 虛擬機器中,在加密和解密之間轉換客戶機記憶體需要與管理程式/VMM 協調。這透過從 __set_memory_enc_pgtable() 呼叫的回撥來實現。在全面感知模式下,使用這些回撥的正常 SEV-SNP 和 TDX 實現。在半虛擬化管理程式模式下,使用 Hyper-V 特定的一組回撥。這些回撥呼叫半虛擬化管理程式,以便半虛擬化管理程式可以協調轉換並根據需要通知管理程式。請參閱 hv_vtom_init() 中這些回撥的設定。

  • 中斷注入。在全面感知模式下,惡意管理程式可能會在違反 x86/x64 架構規則的時間點向客戶機作業系統注入中斷。為了提供全面保護,客戶機作業系統應包含使用 CoCo 處理器提供的中斷注入管理功能的感知功能。在半虛擬化管理程式模式下,半虛擬化管理程式調解向客戶機作業系統注入中斷,並確保客戶機作業系統只看到“合法”中斷。半虛擬化管理程式使用 CoCo 物理處理器提供的中斷注入管理功能,從而向客戶機作業系統隱藏了這些複雜性。

Hyper-V 超級呼叫

在全面感知模式下,Linux 客戶機發出的超級呼叫會直接路由到管理程式,就像在非 CoCo 虛擬機器中一樣。但在半虛擬化管理程式模式下,正常的超級呼叫首先會陷入半虛擬化管理程式,半虛擬化管理程式可能會反過來呼叫管理程式。但半虛擬化管理程式在這方面有其特殊性,Linux 客戶機發出的一些超級呼叫必須始終直接路由到管理程式。這些超級呼叫站點會檢查半虛擬化管理程式是否存在,並使用特殊的呼叫序列。例如,請參閱 hv_post_message()。

客戶機與 Hyper-V 通訊

除了 Linux CoCo 虛擬機器中 Linux 核心對記憶體加密的通用處理之外,Hyper-V 還具有 VMBus 和 VMBus 裝置,它們使用 Linux 客戶機和主機之間共享的記憶體進行通訊。此共享記憶體必須標記為已解密才能啟用通訊。此外,由於威脅模型包括已被攻破和潛在惡意的宿主機,客戶機必須提防透過此共享記憶體將任何非預期資料洩露給宿主機。

這些 Hyper-V 和 VMBus 記憶體頁被標記為已解密

  • VMBus 監控頁

  • 合成中斷控制器 (synic) 相關頁(除非由半虛擬化管理程式提供)

  • 每 CPU 超級呼叫輸入和輸出頁(除非在半虛擬化管理程式下執行)

  • VMBus 環形緩衝區。直接對映在 __vmbus_establish_gpadl() 中標記為已解密。在 hv_ringbuffer_init() 中建立的二級對映也必須包含“decrypted”屬性。

當客戶機將資料寫入與主機共享的記憶體時,它必須確保只寫入預期資料。在複製到共享記憶體之前,填充或未使用的欄位必須初始化為零,以避免隨機核心資料無意中洩露給主機。

同樣,當客戶機讀取與主機共享的記憶體時,它必須在處理資料之前驗證資料,以防止惡意主機誘導客戶機暴露非預期資料。進行此類驗證可能很棘手,因為主機甚至在驗證執行期間或之後都可以修改共享記憶體區域。對於在 VMBus 環形緩衝區中從主機傳遞到客戶機的訊息,會驗證訊息的長度,並將訊息複製到臨時(加密)緩衝區中進行進一步驗證和處理。複製會增加少量開銷,但這是防止惡意主機的唯一方法。請參閱 hv_pkt_iter_first()。

許多 VMBus 裝置的驅動程式已透過新增程式碼來完全驗證透過 VMBus 接收到的訊息而得到“強化”,而不是假定 Hyper-V 是協同工作的。此類驅動程式在 vmbus_devs[] 表中被標記為“allowed_in_isolated”。其他在 CoCo 虛擬機器中不需要的 VMBus 裝置驅動程式尚未強化,並且不允許在 CoCo 虛擬機器中載入。請參閱 vmbus_is_valid_offer() 中排除此類裝置的部分。

兩個 VMBus 裝置依賴 Hyper-V 主機進行 DMA 資料傳輸:用於磁碟 I/O 的 storvsc 和用於網路 I/O 的 netvsc。storvsc 使用正常的 Linux 核心 DMA API,因此透過已解密的 swiotlb 記憶體進行彈跳緩衝是隱式完成的。netvsc 有兩種資料傳輸模式。第一種模式透過由 netvsc 驅動程式顯式分配的傳送和接收緩衝區空間進行,用於大多數較小的包。這些傳送和接收緩衝區由 __vmbus_establish_gpadl() 標記為已解密。由於 netvsc 驅動程式明確地將資料包複製到/從這些緩衝區,因此加密和解密記憶體之間的彈跳緩衝的等效功能已經成為資料路徑的一部分。第二種模式使用正常的 Linux 核心 DMA API,並透過 swiotlb 記憶體隱式地進行彈跳緩衝,就像在 storvsc 中一樣。

最後,VMBus 虛擬 PCI 驅動程式在 CoCo 虛擬機器中需要特殊處理。Linux PCI 裝置驅動程式使用 Linux PCI 子系統提供的標準 API 訪問 PCI 配置空間。在 Hyper-V 上,這些函式直接訪問 MMIO 空間,並且訪問會陷入 Hyper-V 進行模擬。但在 CoCo 虛擬機器中,記憶體加密阻止 Hyper-V 讀取客戶機指令流來模擬訪問。因此,在 CoCo 虛擬機器中,這些函式必須進行超級呼叫,並顯式描述訪問的引數。請參閱 _hv_pcifront_read_config() 和 _hv_pcifront_write_config() 以及指示使用超級呼叫的“use_calls”標誌。

load_unaligned_zeropad()

在加密和解密之間轉換記憶體時,set_memory_encrypted() 或 set_memory_decrypted() 的呼叫者負責確保在轉換進行期間記憶體未在使用且未被引用。轉換涉及多個步驟,幷包括與 Hyper-V 主機的互動。在所有步驟完成之前,記憶體處於不一致狀態。在此狀態不一致時進行引用可能導致無法乾淨修復的異常。

然而,核心的 load_unaligned_zeropad() 機制可能會產生散亂引用,而 set_memory_encrypted() 或 set_memory_decrypted() 的呼叫者無法阻止這些引用,因此 #VC 或 #VE 異常處理程式中有專門的程式碼來修復這種情況。但是,在 Hyper-V 上執行的 CoCo 虛擬機器可能被配置為使用半虛擬化管理程式執行,此時 #VC 或 #VE 異常被路由到半虛擬化管理程式。沒有架構上的方法可以將異常轉發回客戶機核心,在這種情況下,#VC/#VE 處理程式中的 load_unaligned_zeropad() 修復程式碼不會執行。

為避免此問題,Hyper-V 特定功能在轉換進行時將頁面標記為“不存在”,以通知管理程式轉換狀態。如果 load_unaligned_zeropad() 導致散亂引用,將生成正常的頁錯誤而不是 #VC 或 #VE,並且基於頁錯誤的 load_unaligned_zeropad() 處理程式會修復該引用。當加密/解密轉換完成時,頁面再次被標記為“存在”。請參閱 hv_vtom_clear_present() 和 hv_vtom_set_host_visibility()。