Message ID | 20231105163040.14904-34-pbonzini@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | KVM: guest_memfd() and per-page attributes | expand |
On Sun, Nov 5, 2023 at 4:35 PM Paolo Bonzini <pbonzini@redhat.com> wrote: > > From: Ackerley Tng <ackerleytng@google.com> > > "Testing private access when memslot gets deleted" tests the behavior > of KVM when a private memslot gets deleted while the VM is using the > private memslot. When KVM looks up the deleted (slot = NULL) memslot, > KVM should exit to userspace with KVM_EXIT_MEMORY_FAULT. > > In the second test, upon a private access to non-private memslot, KVM > should also exit to userspace with KVM_EXIT_MEMORY_FAULT. nit: The commit message is referring to private memslots, which might need rewording with the latest changes in v14. > Intentionally don't take a requirement on KVM_CAP_GUEST_MEMFD, > KVM_CAP_MEMORY_FAULT_INFO, KVM_MEMORY_ATTRIBUTE_PRIVATE, etc., as it's a > KVM bug to advertise KVM_X86_SW_PROTECTED_VM without its prerequisites. > > Signed-off-by: Ackerley Tng <ackerleytng@google.com> > [sean: call out the similarities with set_memory_region_test] > Signed-off-by: Sean Christopherson <seanjc@google.com> > Message-Id: <20231027182217.3615211-36-seanjc@google.com> > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- > tools/testing/selftests/kvm/Makefile | 1 + > .../kvm/x86_64/private_mem_kvm_exits_test.c | 120 ++++++++++++++++++ > 2 files changed, 121 insertions(+) > create mode 100644 tools/testing/selftests/kvm/x86_64/private_mem_kvm_exits_test.c > > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile > index fd3b30a4ca7b..69ce8e06b3a3 100644 > --- a/tools/testing/selftests/kvm/Makefile > +++ b/tools/testing/selftests/kvm/Makefile > @@ -92,6 +92,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/nested_exceptions_test > TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test > TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test > TEST_GEN_PROGS_x86_64 += x86_64/private_mem_conversions_test > +TEST_GEN_PROGS_x86_64 += x86_64/private_mem_kvm_exits_test > TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id > TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test > TEST_GEN_PROGS_x86_64 += x86_64/smaller_maxphyaddr_emulation_test > diff --git a/tools/testing/selftests/kvm/x86_64/private_mem_kvm_exits_test.c b/tools/testing/selftests/kvm/x86_64/private_mem_kvm_exits_test.c > new file mode 100644 > index 000000000000..2f02f6128482 > --- /dev/null > +++ b/tools/testing/selftests/kvm/x86_64/private_mem_kvm_exits_test.c > @@ -0,0 +1,120 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2022, Google LLC. nit: 2023 Nits aside: Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> Cheers, /fuad > + */ > +#include <linux/kvm.h> > +#include <pthread.h> > +#include <stdint.h> > + > +#include "kvm_util.h" > +#include "processor.h" > +#include "test_util.h" > + > +/* Arbitrarily selected to avoid overlaps with anything else */ > +#define EXITS_TEST_GVA 0xc0000000 > +#define EXITS_TEST_GPA EXITS_TEST_GVA > +#define EXITS_TEST_NPAGES 1 > +#define EXITS_TEST_SIZE (EXITS_TEST_NPAGES * PAGE_SIZE) > +#define EXITS_TEST_SLOT 10 > + > +static uint64_t guest_repeatedly_read(void) > +{ > + volatile uint64_t value; > + > + while (true) > + value = *((uint64_t *) EXITS_TEST_GVA); > + > + return value; > +} > + > +static uint32_t run_vcpu_get_exit_reason(struct kvm_vcpu *vcpu) > +{ > + int r; > + > + r = _vcpu_run(vcpu); > + if (r) { > + TEST_ASSERT(errno == EFAULT, KVM_IOCTL_ERROR(KVM_RUN, r)); > + TEST_ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_MEMORY_FAULT); > + } > + return vcpu->run->exit_reason; > +} > + > +const struct vm_shape protected_vm_shape = { > + .mode = VM_MODE_DEFAULT, > + .type = KVM_X86_SW_PROTECTED_VM, > +}; > + > +static void test_private_access_memslot_deleted(void) > +{ > + struct kvm_vm *vm; > + struct kvm_vcpu *vcpu; > + pthread_t vm_thread; > + void *thread_return; > + uint32_t exit_reason; > + > + vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu, > + guest_repeatedly_read); > + > + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, > + EXITS_TEST_GPA, EXITS_TEST_SLOT, > + EXITS_TEST_NPAGES, > + KVM_MEM_GUEST_MEMFD); > + > + virt_map(vm, EXITS_TEST_GVA, EXITS_TEST_GPA, EXITS_TEST_NPAGES); > + > + /* Request to access page privately */ > + vm_mem_set_private(vm, EXITS_TEST_GPA, EXITS_TEST_SIZE); > + > + pthread_create(&vm_thread, NULL, > + (void *(*)(void *))run_vcpu_get_exit_reason, > + (void *)vcpu); > + > + vm_mem_region_delete(vm, EXITS_TEST_SLOT); > + > + pthread_join(vm_thread, &thread_return); > + exit_reason = (uint32_t)(uint64_t)thread_return; > + > + TEST_ASSERT_EQ(exit_reason, KVM_EXIT_MEMORY_FAULT); > + TEST_ASSERT_EQ(vcpu->run->memory_fault.flags, KVM_MEMORY_EXIT_FLAG_PRIVATE); > + TEST_ASSERT_EQ(vcpu->run->memory_fault.gpa, EXITS_TEST_GPA); > + TEST_ASSERT_EQ(vcpu->run->memory_fault.size, EXITS_TEST_SIZE); > + > + kvm_vm_free(vm); > +} > + > +static void test_private_access_memslot_not_private(void) > +{ > + struct kvm_vm *vm; > + struct kvm_vcpu *vcpu; > + uint32_t exit_reason; > + > + vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu, > + guest_repeatedly_read); > + > + /* Add a non-private memslot (flags = 0) */ > + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, > + EXITS_TEST_GPA, EXITS_TEST_SLOT, > + EXITS_TEST_NPAGES, 0); > + > + virt_map(vm, EXITS_TEST_GVA, EXITS_TEST_GPA, EXITS_TEST_NPAGES); > + > + /* Request to access page privately */ > + vm_mem_set_private(vm, EXITS_TEST_GPA, EXITS_TEST_SIZE); > + > + exit_reason = run_vcpu_get_exit_reason(vcpu); > + > + TEST_ASSERT_EQ(exit_reason, KVM_EXIT_MEMORY_FAULT); > + TEST_ASSERT_EQ(vcpu->run->memory_fault.flags, KVM_MEMORY_EXIT_FLAG_PRIVATE); > + TEST_ASSERT_EQ(vcpu->run->memory_fault.gpa, EXITS_TEST_GPA); > + TEST_ASSERT_EQ(vcpu->run->memory_fault.size, EXITS_TEST_SIZE); > + > + kvm_vm_free(vm); > +} > + > +int main(int argc, char *argv[]) > +{ > + TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM)); > + > + test_private_access_memslot_deleted(); > + test_private_access_memslot_not_private(); > +} > -- > 2.39.1 > >
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index fd3b30a4ca7b..69ce8e06b3a3 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -92,6 +92,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/nested_exceptions_test TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test TEST_GEN_PROGS_x86_64 += x86_64/private_mem_conversions_test +TEST_GEN_PROGS_x86_64 += x86_64/private_mem_kvm_exits_test TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test TEST_GEN_PROGS_x86_64 += x86_64/smaller_maxphyaddr_emulation_test diff --git a/tools/testing/selftests/kvm/x86_64/private_mem_kvm_exits_test.c b/tools/testing/selftests/kvm/x86_64/private_mem_kvm_exits_test.c new file mode 100644 index 000000000000..2f02f6128482 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/private_mem_kvm_exits_test.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022, Google LLC. + */ +#include <linux/kvm.h> +#include <pthread.h> +#include <stdint.h> + +#include "kvm_util.h" +#include "processor.h" +#include "test_util.h" + +/* Arbitrarily selected to avoid overlaps with anything else */ +#define EXITS_TEST_GVA 0xc0000000 +#define EXITS_TEST_GPA EXITS_TEST_GVA +#define EXITS_TEST_NPAGES 1 +#define EXITS_TEST_SIZE (EXITS_TEST_NPAGES * PAGE_SIZE) +#define EXITS_TEST_SLOT 10 + +static uint64_t guest_repeatedly_read(void) +{ + volatile uint64_t value; + + while (true) + value = *((uint64_t *) EXITS_TEST_GVA); + + return value; +} + +static uint32_t run_vcpu_get_exit_reason(struct kvm_vcpu *vcpu) +{ + int r; + + r = _vcpu_run(vcpu); + if (r) { + TEST_ASSERT(errno == EFAULT, KVM_IOCTL_ERROR(KVM_RUN, r)); + TEST_ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_MEMORY_FAULT); + } + return vcpu->run->exit_reason; +} + +const struct vm_shape protected_vm_shape = { + .mode = VM_MODE_DEFAULT, + .type = KVM_X86_SW_PROTECTED_VM, +}; + +static void test_private_access_memslot_deleted(void) +{ + struct kvm_vm *vm; + struct kvm_vcpu *vcpu; + pthread_t vm_thread; + void *thread_return; + uint32_t exit_reason; + + vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu, + guest_repeatedly_read); + + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, + EXITS_TEST_GPA, EXITS_TEST_SLOT, + EXITS_TEST_NPAGES, + KVM_MEM_GUEST_MEMFD); + + virt_map(vm, EXITS_TEST_GVA, EXITS_TEST_GPA, EXITS_TEST_NPAGES); + + /* Request to access page privately */ + vm_mem_set_private(vm, EXITS_TEST_GPA, EXITS_TEST_SIZE); + + pthread_create(&vm_thread, NULL, + (void *(*)(void *))run_vcpu_get_exit_reason, + (void *)vcpu); + + vm_mem_region_delete(vm, EXITS_TEST_SLOT); + + pthread_join(vm_thread, &thread_return); + exit_reason = (uint32_t)(uint64_t)thread_return; + + TEST_ASSERT_EQ(exit_reason, KVM_EXIT_MEMORY_FAULT); + TEST_ASSERT_EQ(vcpu->run->memory_fault.flags, KVM_MEMORY_EXIT_FLAG_PRIVATE); + TEST_ASSERT_EQ(vcpu->run->memory_fault.gpa, EXITS_TEST_GPA); + TEST_ASSERT_EQ(vcpu->run->memory_fault.size, EXITS_TEST_SIZE); + + kvm_vm_free(vm); +} + +static void test_private_access_memslot_not_private(void) +{ + struct kvm_vm *vm; + struct kvm_vcpu *vcpu; + uint32_t exit_reason; + + vm = vm_create_shape_with_one_vcpu(protected_vm_shape, &vcpu, + guest_repeatedly_read); + + /* Add a non-private memslot (flags = 0) */ + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, + EXITS_TEST_GPA, EXITS_TEST_SLOT, + EXITS_TEST_NPAGES, 0); + + virt_map(vm, EXITS_TEST_GVA, EXITS_TEST_GPA, EXITS_TEST_NPAGES); + + /* Request to access page privately */ + vm_mem_set_private(vm, EXITS_TEST_GPA, EXITS_TEST_SIZE); + + exit_reason = run_vcpu_get_exit_reason(vcpu); + + TEST_ASSERT_EQ(exit_reason, KVM_EXIT_MEMORY_FAULT); + TEST_ASSERT_EQ(vcpu->run->memory_fault.flags, KVM_MEMORY_EXIT_FLAG_PRIVATE); + TEST_ASSERT_EQ(vcpu->run->memory_fault.gpa, EXITS_TEST_GPA); + TEST_ASSERT_EQ(vcpu->run->memory_fault.size, EXITS_TEST_SIZE); + + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM)); + + test_private_access_memslot_deleted(); + test_private_access_memslot_not_private(); +}