[PATCH v4 7/7] KVM: selftests: Test guest_memfd behavior with respect to stage 2 page tables
From: Lisa Wang
Date: Tue Jun 02 2026 - 18:02:15 EST
Test that
+ memory failure handling results in unmapping of bad memory from stage
2 page tables, hence requiring faulting on next guest access
+ when the guest tries to fault a poisoned page from guest_memfd, the
userspace VMM informed with EHWPOISON
Co-developed-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
Signed-off-by: Lisa Wang <wyihan@xxxxxxxxxx>
---
.../kvm/guest_memfd_memory_failure_test.c | 66 ++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c b/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c
index 6c8032d390ae..e6f4c327bd5a 100644
--- a/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c
@@ -24,6 +24,7 @@
#include "kvm_util.h"
#include "test_util.h"
#include "kselftest_harness.h"
+#include "ucall_common.h"
static size_t page_size, total_size;
@@ -313,6 +314,71 @@ TEST_F(guest_memfd_failure, test_memory_failure)
}
}
+static void __guest_code_read(uint64_t gpa)
+{
+ uint8_t *mem = (uint8_t *)gpa;
+
+ READ_ONCE(*mem);
+ GUEST_SYNC(0);
+ READ_ONCE(*mem);
+ GUEST_DONE();
+}
+
+static void guest_read(struct kvm_vcpu *vcpu, int expected_errno)
+{
+ if (expected_errno) {
+ TEST_ASSERT_EQ(_vcpu_run(vcpu), -1);
+ TEST_ASSERT_EQ(errno, expected_errno);
+ } else {
+ vcpu_run(vcpu);
+ TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_SYNC);
+ }
+}
+
+TEST_F(guest_memfd_failure, test_memory_failure_guest)
+{
+ const uint64_t gpa = SZ_4G;
+ const int slot = 1;
+
+ unsigned long memory_failure_pfn;
+ struct kvm_vcpu *vcpu;
+ uint8_t *mem;
+
+ /* Limit guest test execution to a single variant to avoid redundant runs. */
+ if (variant->method != MF_INJECT_DEBUGFS ||
+ variant->kill_config != PR_MCE_KILL_EARLY ||
+ !variant->map_page || !variant->dirty_page)
+ return;
+
+ self->vm = __vm_create_shape_with_one_vcpu(VM_SHAPE_DEFAULT, &vcpu, 1, __guest_code_read);
+ vcpu_args_set(vcpu, 1, gpa);
+
+ self->fd = vm_create_guest_memfd(self->vm, self->vm->page_size,
+ GUEST_MEMFD_FLAG_MMAP |
+ GUEST_MEMFD_FLAG_INIT_SHARED);
+ vm_set_user_memory_region2(self->vm, slot, KVM_MEM_GUEST_MEMFD, gpa,
+ self->vm->page_size, NULL, self->fd, 0);
+
+ mem = mmap(NULL, self->vm->page_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, self->fd, 0);
+ TEST_ASSERT(mem != MAP_FAILED, "mmap() for guest_memfd should succeed.");
+ virt_pg_map(self->vm, gpa, gpa);
+
+ /* Fault in page to read pfn, then unmap page for testing. */
+ READ_ONCE(*mem);
+
+ memory_failure_pfn = addr_to_pfn(mem);
+ munmap(mem, self->vm->page_size);
+
+ /* Fault page into stage2 page tables. */
+ guest_read(vcpu, 0);
+
+ self->poisoned_pfn = memory_failure_pfn;
+ mark_memory_failure(memory_failure_pfn, 0);
+
+ guest_read(vcpu, EHWPOISON);
+}
+
static bool can_inject_memory_failure(void)
{
int fd;
--
2.54.0.1013.g208068f2d8-goog