mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
KVM guest_memfd changes for 7.2
- Return -EEXIST instead of -EINVAL if userspace attempts to bind a gmem
range to multiple memslots, and fix the test that was supposed to ensure
KVM returns -EEXIST.
- Treat memslot binding offsets and sizes as unsigned values to fix a bug
where KVM interprets a large "offset + size" as a negative value and allows
a nonsensical offset.
- Use the inode number instead of the page offset for the NUMA interleaving
index to fix a bug where the effective index would jump by two for
consecutive pages (the caller also adds in the page offset).
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEKTobbabEP7vbhhN9OlYIJqCjN/0FAmorOD8ACgkQOlYIJqCj
N/1bTQ//XLFO1tv8sPyJBcQIOjx86V86XUV1i9SDHgzZ4uXBltJZf2AnhU6r62on
wsG0k2P0VCx+6kTLf4r6j2VI4ZipZL7zlAxNDVvFjpyJGTk4giXOs6mqoMaULiJH
d7uKz0Unzyz4Zaqk6t0IIC+N3nm9tzPyFHIQruUX8Oyfw4KJP0JyIKaOv+DqSHZ3
KRgdZCkPREAcIgZn2GpuGja91RJYPaeuEuW5CFBysHBVxIUkgjqis3A3lL0oUXfR
NsR5TthFMPxrP9a8TAkjqgGzHWHOARc0EdwRC/pufqA1wonbp7bi6vssMjkg/G1m
28yUDDkQ4/C3/xLiDDwL3tujip97F0CrxTcuPtXYsfos4fm0tz4dmkUXuDZo9dVn
Bn+r/oN+4Y3TFYvVJauuwTJGmEkkPHAVVUVNTjpuZ+lgg2kIfv+/hgSxR0K6x1lv
zVtvV/NYiIiNlMlMNjz+XEvmLiaFIlDTmA75NhR3qF2rJqngv0qZv7wpoWqg0hLY
bZZhWZXxlL5Hzs9J1ZXY7EPYUIpd/q5/m4fYF/tWCsKS+kki1z+HLgY9bY3tYsGP
EDcwBjPnZ0OUm3GQOzzPVho3jlscCPMkYibzCTVHZpMZNwvW+XJ7wKMVgugEhu+C
vlXAAuQ58McgnaCrt38yx9K555X5sMDzpvCVbwG5A+sou9LXWFA=
=aXdD
-----END PGP SIGNATURE-----
Merge tag 'kvm-x86-gmem-7.2' of https://github.com/kvm-x86/linux into HEAD
KVM guest_memfd changes for 7.2
- Return -EEXIST instead of -EINVAL if userspace attempts to bind a gmem
range to multiple memslots, and fix the test that was supposed to ensure
KVM returns -EEXIST.
- Treat memslot binding offsets and sizes as unsigned values to fix a bug
where KVM interprets a large "offset + size" as a negative value and allows
a nonsensical offset.
- Use the inode number instead of the page offset for the NUMA interleaving
index to fix a bug where the effective index would jump by two for
consecutive pages (the caller also adds in the page offset).
This commit is contained in:
commit
06ba195f13
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
#ifndef __KVM_MM_H__
|
||||
#define __KVM_MM_H__ 1
|
||||
|
||||
#include <linux/kvm.h>
|
||||
#include <linux/kvm_types.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user