diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c b/tools/testing/selftests/kvm/guest_memfd_test.c index 832ef4dfb99f..2233d871a38f 100644 --- a/tools/testing/selftests/kvm/guest_memfd_test.c +++ b/tools/testing/selftests/kvm/guest_memfd_test.c @@ -345,6 +345,16 @@ static void test_invalid_punch_hole(int fd, size_t total_size) } } +static void test_invalid_binding(struct kvm_vm *vm, int fd, size_t size) +{ + int r; + + r = __vm_set_user_memory_region2(vm, 0, KVM_MEM_GUEST_MEMFD, 0, size, 0, + fd, ALIGN_DOWN(INT64_MAX, page_size)); + TEST_ASSERT(r && errno == EINVAL, + "Memslot with out-of-range offset+size should fail"); +} + static void test_create_guest_memfd_invalid_sizes(struct kvm_vm *vm, u64 guest_memfd_flags) { @@ -408,17 +418,26 @@ static void test_guest_memfd_flags(struct kvm_vm *vm) } } -#define __gmem_test(__test, __vm, __flags, __gmem_size) \ +#define ____gmem_test(__test, __vm, __flags, __gmem_size, args...) \ do { \ int fd = vm_create_guest_memfd(__vm, __gmem_size, __flags); \ \ - test_##__test(fd, __gmem_size); \ + test_##__test(args); \ close(fd); \ } while (0) +#define __gmem_test(__test, __vm, __flags, __gmem_size) \ + ____gmem_test(__test, __vm, __flags, __gmem_size, fd, __gmem_size) + #define gmem_test(__test, __vm, __flags) \ __gmem_test(__test, __vm, __flags, page_size * 4) +#define __gmem_test_vm(__test, __vm, __flags, __gmem_size) \ + ____gmem_test(__test, __vm, __flags, __gmem_size, __vm, fd, __gmem_size) + +#define gmem_test_vm(__test, __vm, __flags) \ + __gmem_test_vm(__test, __vm, __flags, page_size * 4) + static void __test_guest_memfd(struct kvm_vm *vm, u64 flags) { test_create_guest_memfd_multiple(vm); @@ -447,6 +466,7 @@ static void __test_guest_memfd(struct kvm_vm *vm, u64 flags) gmem_test(file_size, vm, flags); gmem_test(fallocate, vm, flags); gmem_test(invalid_punch_hole, vm, flags); + gmem_test_vm(invalid_binding, vm, flags); } static void test_guest_memfd(unsigned long vm_type) diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index e639a9db51ee..a152ab65c657 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -510,7 +510,7 @@ static void test_add_overlapping_private_memory_regions(void) vm = vm_create_barebones_type(KVM_X86_SW_PROTECTED_VM); - memfd = vm_create_guest_memfd(vm, MEM_REGION_SIZE * 4, 0); + memfd = vm_create_guest_memfd(vm, MEM_REGION_SIZE * 5, 0); vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, MEM_REGION_GPA, MEM_REGION_SIZE * 2, 0, memfd, 0); @@ -526,12 +526,35 @@ static void test_add_overlapping_private_memory_regions(void) vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, MEM_REGION_GPA, 0, NULL, -1, 0); - /* Overlap the front half of the other slot. */ + /* + * Verify that overlap in the guest_memfd bindings (i.e. in guest_memfd + * file offsets), but _not_ in the GPA space, fails with -EEXIST. + */ + r = __vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, + MEM_REGION_GPA, + MEM_REGION_SIZE * 2, + 0, memfd, MEM_REGION_SIZE); + TEST_ASSERT(r == -1 && errno == EEXIST, + "Overlapping guest_memfd() bindings should fail with EEXIST"); + + /* And now the back half of the other slot's guest_memfd binding. */ + r = __vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, + MEM_REGION_GPA, + MEM_REGION_SIZE * 2, + 0, memfd, MEM_REGION_SIZE * 3); + TEST_ASSERT(r == -1 && errno == EEXIST, + "Overlapping guest_memfd() bindings should fail with EEXIST"); + + /* + * Repeat the overlap tests, but this time with overlap in the memslots + * GPA space. Regardless of where there is overlap, KVM should return + * -EEXIST. + */ r = __vm_set_user_memory_region2(vm, MEM_REGION_SLOT, KVM_MEM_GUEST_MEMFD, MEM_REGION_GPA * 2 - MEM_REGION_SIZE, MEM_REGION_SIZE * 2, 0, memfd, 0); - TEST_ASSERT(r == -1 && errno == EEXIST, "%s", + TEST_ASSERT(r == -1 && errno == EEXIST, "Overlapping guest_memfd() bindings should fail with EEXIST"); /* And now the back half of the other slot. */ @@ -539,7 +562,7 @@ static void test_add_overlapping_private_memory_regions(void) MEM_REGION_GPA * 2 + MEM_REGION_SIZE, MEM_REGION_SIZE * 2, 0, memfd, 0); - TEST_ASSERT(r == -1 && errno == EEXIST, "%s", + TEST_ASSERT(r == -1 && errno == EEXIST, "Overlapping guest_memfd() bindings should fail with EEXIST"); close(memfd); diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index a35a55571a2d..e9cdda170a49 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -438,11 +438,12 @@ static int kvm_gmem_set_policy(struct vm_area_struct *vma, struct mempolicy *mpo } static struct mempolicy *kvm_gmem_get_policy(struct vm_area_struct *vma, - unsigned long addr, pgoff_t *pgoff) + unsigned long addr, pgoff_t *ilx) { + pgoff_t pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT); struct inode *inode = file_inode(vma->vm_file); - *pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT); + *ilx = inode->i_ino; /* * Return the memory policy for this index, or NULL if none is set. @@ -453,7 +454,7 @@ static struct mempolicy *kvm_gmem_get_policy(struct vm_area_struct *vma, * can then replace NULL with the default memory policy instead of the * current task's memory policy. */ - return mpol_shared_policy_lookup(&GMEM_I(inode)->policy, *pgoff); + return mpol_shared_policy_lookup(&GMEM_I(inode)->policy, pgoff); } #endif /* CONFIG_NUMA */ @@ -640,15 +641,16 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args) } int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset) + unsigned int fd, uoff_t offset) { - loff_t size = slot->npages << PAGE_SHIFT; + uoff_t size = slot->npages << PAGE_SHIFT; unsigned long start, end; struct gmem_file *f; struct inode *inode; struct file *file; int r = -EINVAL; + BUILD_BUG_ON(sizeof(gpa_t) != sizeof(offset)); BUILD_BUG_ON(sizeof(gfn_t) != sizeof(slot->gmem.pgoff)); file = fget(fd); @@ -664,8 +666,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, inode = file_inode(file); - if (offset < 0 || !PAGE_ALIGNED(offset) || - offset + size > i_size_read(inode)) + if (!PAGE_ALIGNED(offset) || offset + size > i_size_read(inode)) goto err; filemap_invalidate_lock(inode->i_mapping); @@ -675,6 +676,7 @@ int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, if (!xa_empty(&f->bindings) && xa_find(&f->bindings, &start, end - 1, XA_PRESENT)) { + r = -EEXIST; filemap_invalidate_unlock(inode->i_mapping); goto err; } diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h index 9fcc5d5b7f8d..7510ca915dd1 100644 --- a/virt/kvm/kvm_mm.h +++ b/virt/kvm/kvm_mm.h @@ -3,6 +3,9 @@ #ifndef __KVM_MM_H__ #define __KVM_MM_H__ 1 +#include +#include + /* * Architectures can choose whether to use an rwlock or spinlock * for the mmu_lock. These macros, for use in common code @@ -72,7 +75,7 @@ int kvm_gmem_init(struct module *module); void kvm_gmem_exit(void); int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args); int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset); + unsigned int fd, uoff_t offset); void kvm_gmem_unbind(struct kvm_memory_slot *slot); #else static inline int kvm_gmem_init(struct module *module) @@ -82,7 +85,7 @@ static inline int kvm_gmem_init(struct module *module) static inline void kvm_gmem_exit(void) {}; static inline int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot, - unsigned int fd, loff_t offset) + unsigned int fd, uoff_t offset) { WARN_ON_ONCE(1); return -EIO;