[PATCH v3 15/15] OvfmPkg/VmgExitLib: Validate #VC MMIO is to un-encrypted memory #vc


Lendacky, Thomas
 

From: Tom Lendacky <thomas.lendacky@...>

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=3108

When SEV-ES is active, and MMIO operation will trigger a #VC and the
VmgExitLib exception handler will process this MMIO operation.

A malicious hypervisor could try to extract information from encrypted
memory by setting a reserved bit in the guests nested page tables for
a non-MMIO area. This can result in the encrypted data being copied into
the GHCB shared buffer area and accessed by the hypervisor.

Prevent this by ensuring that the MMIO source/destination is un-encrypted
memory. For the APIC register space, access is allowed in general.

Cc: Jordan Justen <jordan.l.justen@...>
Cc: Laszlo Ersek <lersek@...>
Cc: Ard Biesheuvel <ard.biesheuvel@...>
Cc: Brijesh Singh <brijesh.singh@...>
Acked-by: Laszlo Ersek <lersek@...>
Signed-off-by: Tom Lendacky <thomas.lendacky@...>
---
OvmfPkg/AmdSev/AmdSevX64.dsc | 1 +
OvmfPkg/OvmfPkgX64.dsc | 1 +
OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf | 2 +-
OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf | 2 +
OvmfPkg/Library/VmgExitLib/VmgExitLib.inf | 2 +
OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c | 81 ++++++++++++++++++++
6 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/OvmfPkg/AmdSev/AmdSevX64.dsc b/OvmfPkg/AmdSev/AmdSevX64.dsc
index c4d93f39b9f1..dad8635c3388 100644
--- a/OvmfPkg/AmdSev/AmdSevX64.dsc
+++ b/OvmfPkg/AmdSev/AmdSevX64.dsc
@@ -237,6 +237,7 @@ [LibraryClasses.common.SEC]
CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
!endif
VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
+ MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf

[LibraryClasses.common.PEI_CORE]
HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index bfa9dd7cac1f..70ff2bcf2342 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -266,6 +266,7 @@ [LibraryClasses.common.SEC]
CpuExceptionHandlerLib|UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
!endif
VmgExitLib|OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
+ MemEncryptSevLib|OvmfPkg/Library/BaseMemEncryptSevLib/SecMemEncryptSevLib.inf

[LibraryClasses.common.PEI_CORE]
HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
diff --git a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
index 8e3b8ddd5a95..f2e162d68076 100644
--- a/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
+++ b/OvmfPkg/Library/BaseMemEncryptSevLib/DxeMemEncryptSevLib.inf
@@ -14,7 +14,7 @@ [Defines]
FILE_GUID = c1594631-3888-4be4-949f-9c630dbc842b
MODULE_TYPE = BASE
VERSION_STRING = 1.0
- LIBRARY_CLASS = MemEncryptSevLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER
+ LIBRARY_CLASS = MemEncryptSevLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_DRIVER

#
# The following information is for reference only and not required by the build
diff --git a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
index df14de3c21bc..e6f6ea7972fd 100644
--- a/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/SecVmgExitLib.inf
@@ -35,6 +35,8 @@ [LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
+ LocalApicLib
+ MemEncryptSevLib
PcdLib

[FixedPcd]
diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
index b3c3e56ecff8..c66c68726cdb 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitLib.inf
@@ -35,4 +35,6 @@ [LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
+ LocalApicLib
+ MemEncryptSevLib

diff --git a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
index ce577e4677eb..24259060fd65 100644
--- a/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
+++ b/OvmfPkg/Library/VmgExitLib/VmgExitVcHandler.c
@@ -9,6 +9,7 @@
#include <Base.h>
#include <Uefi.h>
#include <Library/BaseMemoryLib.h>
+#include <Library/LocalApicLib.h>
#include <Library/MemEncryptSevLib.h>
#include <Library/VmgExitLib.h>
#include <Register/Amd/Msr.h>
@@ -595,6 +596,61 @@ UnsupportedExit (
return Status;
}

+/**
+ Validate that the MMIO memory access is not to encrypted memory.
+
+ Examine the pagetable entry for the memory specified. MMIO should not be
+ performed against encrypted memory. MMIO to the APIC page is always allowed.
+
+ @param[in] Ghcb Pointer to the Guest-Hypervisor Communication Block
+ @param[in] MemoryAddress Memory address to validate
+ @param[in] MemoryLength Memory length to validate
+
+ @retval 0 Memory is not encrypted
+ @return New exception value to propogate
+
+**/
+STATIC
+UINT64
+ValidateMmioMemory (
+ IN GHCB *Ghcb,
+ IN UINTN MemoryAddress,
+ IN UINTN MemoryLength
+ )
+{
+ MEM_ENCRYPT_SEV_ADDRESS_RANGE_STATE State;
+ GHCB_EVENT_INJECTION GpEvent;
+ UINTN Address;
+
+ //
+ // Allow APIC accesses (which will have the encryption bit set during
+ // SEC and PEI phases).
+ //
+ Address = MemoryAddress & ~(SIZE_4KB - 1);
+ if (Address == GetLocalApicBaseAddress ()) {
+ return 0;
+ }
+
+ State = MemEncryptSevGetAddressRangeState (
+ 0,
+ MemoryAddress,
+ MemoryLength
+ );
+ if (State == MemEncryptSevAddressRangeUnencrypted) {
+ return 0;
+ }
+
+ //
+ // Any state other than unencrypted is an error, issue a #GP.
+ //
+ GpEvent.Uint64 = 0;
+ GpEvent.Elements.Vector = GP_EXCEPTION;
+ GpEvent.Elements.Type = GHCB_EVENT_INJECTION_TYPE_EXCEPTION;
+ GpEvent.Elements.Valid = 1;
+
+ return GpEvent.Uint64;
+}
+
/**
Handle an MMIO event.

@@ -653,6 +709,11 @@ MmioExit (
return UnsupportedExit (Ghcb, Regs, InstructionData);
}

+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
ExitInfo1 = InstructionData->Ext.RmData;
ExitInfo2 = Bytes;
CopyMem (Ghcb->SharedBuffer, &InstructionData->Ext.RegData, Bytes);
@@ -683,6 +744,11 @@ MmioExit (
InstructionData->ImmediateSize = Bytes;
InstructionData->End += Bytes;

+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
ExitInfo1 = InstructionData->Ext.RmData;
ExitInfo2 = Bytes;
CopyMem (Ghcb->SharedBuffer, InstructionData->Immediate, Bytes);
@@ -717,6 +783,11 @@ MmioExit (
return UnsupportedExit (Ghcb, Regs, InstructionData);
}

+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
ExitInfo1 = InstructionData->Ext.RmData;
ExitInfo2 = Bytes;

@@ -748,6 +819,11 @@ MmioExit (
case 0xB7:
Bytes = (Bytes != 0) ? Bytes : 2;

+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
ExitInfo1 = InstructionData->Ext.RmData;
ExitInfo2 = Bytes;

@@ -774,6 +850,11 @@ MmioExit (
case 0xBF:
Bytes = (Bytes != 0) ? Bytes : 2;

+ Status = ValidateMmioMemory (Ghcb, InstructionData->Ext.RmData, Bytes);
+ if (Status != 0) {
+ return Status;
+ }
+
ExitInfo1 = InstructionData->Ext.RmData;
ExitInfo2 = Bytes;

--
2.30.0