From: Ackerley Tng Test that + Invalid inputs should be rejected with EINVAL + Successful inputs return a new (destination) fd + Destination and source fds have the same inode number + No crash on program exit Signed-off-by: Ackerley Tng Co-developed-by: Ryan Afranji Signed-off-by: Ryan Afranji Co-developed-by: Tao Gong Signed-off-by: Tao Gong --- .../testing/selftests/kvm/guest_memfd_test.c | 64 +++++++++++++++++++ .../testing/selftests/kvm/include/kvm_util.h | 18 ++++++ 2 files changed, 82 insertions(+) diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index 2233d871a38f4..ccde4d735c77c 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -418,6 +418,68 @@ static void test_guest_memfd_flags(struct kvm_vm *vm) } } +static void test_link(struct kvm_vm *vm, int src_fd, size_t total_size) +{ + struct kvm_link_guest_memfd params = {}; + struct stat src_stat, dst_stat; + struct kvm_vm *dst_vm; + int dst_fd, bad_fd; + int ret; + + /* guest_memfd can only be linked between VMs of the same type. */ + dst_vm = vm_create_barebones_type(vm->type); + + /* Linking with an fd that isn't open */ + bad_fd = dup(src_fd); + TEST_ASSERT(bad_fd >= 0, "dup() failed"); + close(bad_fd); + ret = __vm_link_guest_memfd(dst_vm, bad_fd, 0); + TEST_ASSERT_EQ(ret, -1); + TEST_ASSERT_EQ(errno, EINVAL); + + /* Linking with an out-of-range fd, i.e. KVM must not truncate fd */ + params.fd = (1ULL << 32) | src_fd; + ret = __vm_ioctl(dst_vm, KVM_LINK_GUEST_MEMFD, ¶ms); + TEST_ASSERT_EQ(ret, -1); + TEST_ASSERT_EQ(errno, EINVAL); + + /* Linking with a non-gmem fd */ + ret = __vm_link_guest_memfd(dst_vm, dst_vm->fd, 0); + TEST_ASSERT_EQ(ret, -1); + TEST_ASSERT_EQ(errno, EINVAL); + + /* Linking with invalid flags */ + ret = __vm_link_guest_memfd(dst_vm, src_fd, 1); + TEST_ASSERT_EQ(ret, -1); + TEST_ASSERT_EQ(errno, EINVAL); + + /* Linking with non-zero reserved fields */ + params.fd = src_fd; + params.reserved[5] = 1; + ret = __vm_ioctl(dst_vm, KVM_LINK_GUEST_MEMFD, ¶ms); + TEST_ASSERT_EQ(ret, -1); + TEST_ASSERT_EQ(errno, EINVAL); + + /* Linking with an already-associated vm */ + ret = __vm_link_guest_memfd(vm, src_fd, 0); + TEST_ASSERT_EQ(ret, -1); + TEST_ASSERT_EQ(errno, EINVAL); + + dst_fd = __vm_link_guest_memfd(dst_vm, src_fd, 0); + TEST_ASSERT(dst_fd >= 0, "linking should succeed with valid inputs"); + TEST_ASSERT(src_fd != dst_fd, "linking should return a different fd"); + + ret = fstat(src_fd, &src_stat); + TEST_ASSERT_EQ(ret, 0); + ret = fstat(dst_fd, &dst_stat); + TEST_ASSERT_EQ(ret, 0); + TEST_ASSERT(src_stat.st_ino == dst_stat.st_ino, + "src and dst files should have the same inode number"); + + close(dst_fd); + kvm_vm_free(dst_vm); +} + #define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \ do { \ int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \ @@ -467,6 +529,8 @@ static void __test_guest_memfd(struct kvm_vm *vm, u64 flags) gmem_test(fallocate, vm, flags); gmem_test(invalid_punch_hole, vm, flags); gmem_test_vm(invalid_binding, vm, flags); + if (kvm_has_cap(KVM_CAP_GUEST_MEMFD_LINK)) + gmem_test_vm(link, vm, flags); } static void test_guest_memfd(unsigned long vm_type) diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 777fa3dbf88d6..f8eca3f7993d0 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h @@ -787,6 +787,24 @@ static inline int vm_create_guest_memfd(struct kvm_vm *vm, u64 size, return fd; } +static inline int __vm_link_guest_memfd(struct kvm_vm *vm, int fd, u64 flags) +{ + struct kvm_link_guest_memfd params = { + .fd = fd, + .flags = flags, + }; + + return __vm_ioctl(vm, KVM_LINK_GUEST_MEMFD, ¶ms); +} + +static inline int vm_link_guest_memfd(struct kvm_vm *vm, int fd, u64 flags) +{ + int new_fd = __vm_link_guest_memfd(vm, fd, flags); + + TEST_ASSERT(new_fd >= 0, KVM_IOCTL_ERROR(KVM_LINK_GUEST_MEMFD, new_fd)); + return new_fd; +} + void vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags, gpa_t gpa, u64 size, void *hva); int __vm_set_user_memory_region(struct kvm_vm *vm, u32 slot, u32 flags, -- 2.56.0.257.gf7497c7d27-goog