Skip to content

Commit 66155de

Browse files
sean-jcbonzini
authored andcommitted
KVM: x86: Disallow read-only memslots for SEV-ES and SEV-SNP (and TDX)
Disallow read-only memslots for SEV-{ES,SNP} VM types, as KVM can't directly emulate instructions for ES/SNP, and instead the guest must explicitly request emulation. Unless the guest explicitly requests emulation without accessing memory, ES/SNP relies on KVM creating an MMIO SPTE, with the subsequent #NPF being reflected into the guest as a #VC. But for read-only memslots, KVM deliberately doesn't create MMIO SPTEs, because except for ES/SNP, doing so requires setting reserved bits in the SPTE, i.e. the SPTE can't be readable while also generating a #VC on writes. Because KVM never creates MMIO SPTEs and jumps directly to emulation, the guest never gets a #VC. And since KVM simply resumes the guest if ES/SNP guests trigger emulation, KVM effectively puts the vCPU into an infinite #NPF loop if the vCPU attempts to write read-only memory. Disallow read-only memory for all VMs with protected state, i.e. for upcoming TDX VMs as well as ES/SNP VMs. For TDX, it's actually possible to support read-only memory, as TDX uses EPT Violation #VE to reflect the fault into the guest, e.g. KVM could configure read-only SPTEs with RX protections and SUPPRESS_VE=0. But there is no strong use case for supporting read-only memslots on TDX, e.g. the main historical usage is to emulate option ROMs, but TDX disallows executing from shared memory. And if someone comes along with a legitimate, strong use case, the restriction can always be lifted for TDX. Don't bother trying to retroactively apply the restriction to SEV-ES VMs that are created as type KVM_X86_DEFAULT_VM. Read-only memslots can't possibly work for SEV-ES, i.e. disallowing such memslots is really just means reporting an error to userspace instead of silently hanging vCPUs. Trying to deal with the ordering between KVM_SEV_INIT and memslot creation isn't worth the marginal benefit it would provide userspace. Fixes: 26c44aa ("KVM: SEV: define VM types for SEV and SEV-ES") Fixes: 1dfe571 ("KVM: SEV: Add initial SEV-SNP support") Cc: Peter Gonda <pgonda@google.com> Cc: Michael Roth <michael.roth@amd.com> Cc: Vishal Annapurve <vannapurve@google.com> Cc: Ackerly Tng <ackerleytng@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Message-ID: <20240809190319.1710470-2-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent c9b35a6 commit 66155de

File tree

3 files changed

+11
-3
lines changed

3 files changed

+11
-3
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,8 @@ void kvm_configure_mmu(bool enable_tdp, int tdp_forced_root_level,
21922192
#define kvm_arch_has_private_mem(kvm) false
21932193
#endif
21942194

2195+
#define kvm_arch_has_readonly_mem(kvm) (!(kvm)->arch.has_protected_state)
2196+
21952197
static inline u16 kvm_read_ldt(void)
21962198
{
21972199
u16 ldt;

include/linux/kvm_host.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,13 @@ static inline bool kvm_arch_has_private_mem(struct kvm *kvm)
715715
}
716716
#endif
717717

718+
#ifndef kvm_arch_has_readonly_mem
719+
static inline bool kvm_arch_has_readonly_mem(struct kvm *kvm)
720+
{
721+
return IS_ENABLED(CONFIG_HAVE_KVM_READONLY_MEM);
722+
}
723+
#endif
724+
718725
struct kvm_memslots {
719726
u64 generation;
720727
atomic_long_t last_used_slot;

virt/kvm/kvm_main.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,15 +1578,14 @@ static int check_memory_region_flags(struct kvm *kvm,
15781578
if (mem->flags & KVM_MEM_GUEST_MEMFD)
15791579
valid_flags &= ~KVM_MEM_LOG_DIRTY_PAGES;
15801580

1581-
#ifdef CONFIG_HAVE_KVM_READONLY_MEM
15821581
/*
15831582
* GUEST_MEMFD is incompatible with read-only memslots, as writes to
15841583
* read-only memslots have emulated MMIO, not page fault, semantics,
15851584
* and KVM doesn't allow emulated MMIO for private memory.
15861585
*/
1587-
if (!(mem->flags & KVM_MEM_GUEST_MEMFD))
1586+
if (kvm_arch_has_readonly_mem(kvm) &&
1587+
!(mem->flags & KVM_MEM_GUEST_MEMFD))
15881588
valid_flags |= KVM_MEM_READONLY;
1589-
#endif
15901589

15911590
if (mem->flags & ~valid_flags)
15921591
return -EINVAL;

0 commit comments

Comments
 (0)