[PATCH 10/24] KVM: Include memory protections in result of gfn->hva conversion
From: Paolo Bonzini
Date: Thu Jul 16 2026 - 14:25:47 EST
From: Nicolas Saenz Julienne <nsaenz@xxxxxxxxxx>
All paths that lead to guest memory accesses now need to check whether
memory attributes allow that access. Users of gfn_to_hva,
kvm_vcpu_gfn_to_hva and their *_prot variant can get it more or
less for free via erroneous return values, so do this first.
Note however that this is not true of the variants that take a cached
kvm_memslots pointer. These include caches (gfn-to-hva and gfn-to-pfn)
and page faults, both of which will need specific changes; but the more
optimized functions in virt/kvm/kvm_main.c such as kvm_read_guest()
and kvm_write_guest() also retrieve the memslot high in the call chain,
and therefore they will need changes in __kvm_read/write_guest_page().
Signed-off-by: Nicolas Saenz Julienne <nsaenz@xxxxxxxxxx>
Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
include/linux/kvm_host.h | 18 +++++++++++++
virt/kvm/kvm_main.c | 55 +++++++++++++++++++++++++++++++++++++---
2 files changed, 69 insertions(+), 4 deletions(-)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 2dd1b65799e4..f10ab70b99dc 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2593,8 +2593,26 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn)
{
return false;
}
+static inline unsigned long kvm_get_memory_attributes(struct kvm *kvm, gfn_t gfn)
+{
+ return 0;
+}
#endif /* CONFIG_KVM_GENERIC_MEMORY_ATTRIBUTES */
+static inline int kvm_mem_attributes_may_read_gfn(struct kvm *kvm, gfn_t gfn)
+{
+ unsigned long attrs = kvm_get_memory_attributes(kvm, gfn);
+
+ return kvm_mem_attributes_may_read(attrs);
+}
+
+static inline int kvm_mem_attributes_may_write_gfn(struct kvm *kvm, gfn_t gfn)
+{
+ unsigned long attrs = kvm_get_memory_attributes(kvm, gfn);
+
+ return kvm_mem_attributes_may_write(attrs);
+}
+
#ifdef CONFIG_KVM_GUEST_MEMFD
int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
gfn_t gfn, kvm_pfn_t *pfn, struct page **page,
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index a47b62c2c9ce..56016aab0aad 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2764,13 +2764,38 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(gfn_to_hva_memslot);
unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn)
{
- return gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL);
+ unsigned long addr;
+
+ addr = gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL);
+ if (kvm_is_error_hva(addr))
+ return addr;
+
+ if (!kvm_mem_attributes_may_read_gfn(kvm, gfn))
+ return KVM_HVA_ERR_BAD;
+
+ if (!kvm_mem_attributes_may_write_gfn(kvm, gfn))
+ return KVM_HVA_ERR_RO_BAD;
+
+ return addr;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(gfn_to_hva);
unsigned long kvm_vcpu_gfn_to_hva(struct kvm_vcpu *vcpu, gfn_t gfn)
{
- return gfn_to_hva_many(kvm_vcpu_gfn_to_memslot(vcpu, gfn), gfn, NULL);
+ struct kvm *kvm = vcpu->kvm;
+ unsigned long addr;
+
+ addr = gfn_to_hva_many(kvm_vcpu_gfn_to_memslot(vcpu, gfn), gfn, NULL);
+ if (kvm_is_error_hva(addr))
+ return addr;
+
+ if (!kvm_mem_attributes_may_read_gfn(kvm, gfn))
+ return KVM_HVA_ERR_BAD;
+
+ if (!kvm_mem_attributes_may_write_gfn(kvm, gfn))
+ return KVM_HVA_ERR_RO_BAD;
+
+ return addr;
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_vcpu_gfn_to_hva);
@@ -2796,15 +2821,37 @@ unsigned long gfn_to_hva_memslot_prot(struct kvm_memory_slot *slot,
unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable)
{
struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
+ unsigned long addr;
- return gfn_to_hva_memslot_prot(slot, gfn, writable);
+ addr = gfn_to_hva_memslot_prot(slot, gfn, writable);
+ if (kvm_is_error_hva(addr))
+ return addr;
+
+ if (!kvm_mem_attributes_may_read_gfn(kvm, gfn))
+ return KVM_HVA_ERR_BAD;
+
+ if (!kvm_mem_attributes_may_write_gfn(kvm, gfn))
+ *writable = false;
+
+ return addr;
}
unsigned long kvm_vcpu_gfn_to_hva_prot(struct kvm_vcpu *vcpu, gfn_t gfn, bool *writable)
{
struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+ unsigned long addr;
- return gfn_to_hva_memslot_prot(slot, gfn, writable);
+ addr = gfn_to_hva_memslot_prot(slot, gfn, writable);
+ if (kvm_is_error_hva(addr))
+ return addr;
+
+ if (!kvm_mem_attributes_may_read_gfn(vcpu->kvm, gfn))
+ return KVM_HVA_ERR_BAD;
+
+ if (!kvm_mem_attributes_may_write_gfn(vcpu->kvm, gfn))
+ *writable = false;
+
+ return addr;
}
static bool kvm_is_ad_tracked_page(struct page *page)
--
2.52.0