History

KVM: guest_memfd: Add INIT_SHARED flag, reject user page faults if not set

Add a guest_memfd flag to allow userspace to state that the underlying memory should be configured to be initialized as shared, and reject user page faults if the guest_memfd instance's memory isn't shared. Because KVM doesn't yet support in-place private<=>shared conversions, all guest_memfd memory effectively follows the initial state. Alternatively, KVM could deduce the initial state based on MMAP, which for all intents and purposes is what KVM currently does. However, implicitly deriving the default state based on MMAP will result in a messy ABI when support for in-place conversions is added. For x86 CoCo VMs, which don't yet support MMAP, memory is currently private by default (otherwise the memory would be unusable). If MMAP implies memory is shared by default, then the default state for CoCo VMs will vary based on MMAP, and from userspace's perspective, will change when in-place conversion support is added. I.e. to maintain guest<=>host ABI, userspace would need to immediately convert all memory from shared=>private, which is both ugly and inefficient. The inefficiency could be avoided by adding a flag to state that memory is _private_ by default, irrespective of MMAP, but that would lead to an equally messy and hard to document ABI. Bite the bullet and immediately add a flag to control the default state so that the effective behavior is explicit and straightforward.

Sean Christopherson committed 1 year ago

Commit fe2bf621 parent d2042d85 files · +26 −6Browse files

Unified · Split

5 files
  1. Documentation/virt/kvm/api.rst +5 −0
  2. include/uapi/linux/kvm.h +2 −1
  3. tools/testing/selftests/kvm/guest_memfd_test.c +12 −3
  4. virt/kvm/guest_memfd.c +5 −1
  5. virt/kvm/kvm_main.c +2 −1
Documentation/virt/kvm/api.rst+5−0
@@ -6438,6 +6438,11 @@ specified via KVM_CREATE_GUEST_MEMFD. Currently defined flags: · 6437 unmodified lines
6438 ============================ ================================================6438 ============================ ================================================
6439 GUEST_MEMFD_FLAG_MMAP Enable using mmap() on the guest_memfd file6439 GUEST_MEMFD_FLAG_MMAP Enable using mmap() on the guest_memfd file
6440 descriptor.6440 descriptor.
6441 GUEST_MEMFD_FLAG_INIT_SHARED Make all memory in the file shared during
6442 KVM_CREATE_GUEST_MEMFD (memory files created
6443 without INIT_SHARED will be marked private).
6444 Shared memory can be faulted into host userspace
6445 page tables. Private memory cannot.
6441 ============================ ================================================6446 ============================ ================================================
6442 6447
6443When the KVM MMU performs a PFN lookup to service a guest fault and the backing6448When the KVM MMU performs a PFN lookup to service a guest fault and the backing
include/uapi/linux/kvm.h+2−1
@@ -1599,7 +1599,8 @@ struct kvm_memory_attributes { · 1598 unmodified lines
1599#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3)1599#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3)
1600 1600
1601#define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd)1601#define KVM_CREATE_GUEST_MEMFD _IOWR(KVMIO, 0xd4, struct kvm_create_guest_memfd)
1602#define GUEST_MEMFD_FLAG_MMAP (1ULL << 0)1602#define GUEST_MEMFD_FLAG_MMAP (1ULL << 0)
1603#define GUEST_MEMFD_FLAG_INIT_SHARED (1ULL << 1)
1603 1604
1604struct kvm_create_guest_memfd {1605struct kvm_create_guest_memfd {
1605 __u64 size;1606 __u64 size;
tools/testing/selftests/kvm/guest_memfd_test.c+12−3
@@ -239,8 +239,9 @@ static void test_create_guest_memfd_multiple(struct kvm_vm *vm) · 238 unmodified lines
239 close(fd1);239 close(fd1);
240}240}
241 241
242static void test_guest_memfd_flags(struct kvm_vm *vm, uint64_t valid_flags)242static void test_guest_memfd_flags(struct kvm_vm *vm)
243{243{
244 uint64_t valid_flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);
244 size_t page_size = getpagesize();245 size_t page_size = getpagesize();
245 uint64_t flag;246 uint64_t flag;
246 int fd;247 int fd;
@@ -274,6 +275,10 @@ static void test_guest_memfd(unsigned long vm_type) · 27 unmodified lines
274 vm = vm_create_barebones_type(vm_type);275 vm = vm_create_barebones_type(vm_type);
275 flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);276 flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);
276 277
278 /* This test doesn't yet support testing mmap() on private memory. */
279 if (!(flags & GUEST_MEMFD_FLAG_INIT_SHARED))
280 flags &= ~GUEST_MEMFD_FLAG_MMAP;
281
277 test_create_guest_memfd_multiple(vm);282 test_create_guest_memfd_multiple(vm);
278 test_create_guest_memfd_invalid_sizes(vm, flags, page_size);283 test_create_guest_memfd_invalid_sizes(vm, flags, page_size);
279 284
@@ -292,7 +297,7 @@ static void test_guest_memfd(unsigned long vm_type) · 12 unmodified lines
292 test_fallocate(fd, page_size, total_size);297 test_fallocate(fd, page_size, total_size);
293 test_invalid_punch_hole(fd, page_size, total_size);298 test_invalid_punch_hole(fd, page_size, total_size);
294 299
295 test_guest_memfd_flags(vm, flags);300 test_guest_memfd_flags(vm);
296 301
297 close(fd);302 close(fd);
298 kvm_vm_free(vm);303 kvm_vm_free(vm);
@@ -334,9 +339,13 @@ static void test_guest_memfd_guest(void) · 35 unmodified lines
334 TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_MMAP,339 TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_MMAP,
335 "Default VM type should support MMAP, supported flags = 0x%x",340 "Default VM type should support MMAP, supported flags = 0x%x",
336 vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));341 vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));
342 TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_INIT_SHARED,
343 "Default VM type should support INIT_SHARED, supported flags = 0x%x",
344 vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS));
337 345
338 size = vm->page_size;346 size = vm->page_size;
339 fd = vm_create_guest_memfd(vm, size, GUEST_MEMFD_FLAG_MMAP);347 fd = vm_create_guest_memfd(vm, size, GUEST_MEMFD_FLAG_MMAP |
348 GUEST_MEMFD_FLAG_INIT_SHARED);
340 vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0);349 vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0);
341 350
342 mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);351 mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
virt/kvm/guest_memfd.c+5−1
@@ -328,6 +328,9 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf) · 327 unmodified lines
328 if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode))328 if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode))
329 return VM_FAULT_SIGBUS;329 return VM_FAULT_SIGBUS;
330 330
331 if (!((u64)inode->i_private & GUEST_MEMFD_FLAG_INIT_SHARED))
332 return VM_FAULT_SIGBUS;
333
331 folio = kvm_gmem_get_folio(inode, vmf->pgoff);334 folio = kvm_gmem_get_folio(inode, vmf->pgoff);
332 if (IS_ERR(folio)) {335 if (IS_ERR(folio)) {
333 int err = PTR_ERR(folio);336 int err = PTR_ERR(folio);
@@ -525,7 +528,8 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args) · 191 unmodified lines
525 u64 valid_flags = 0;528 u64 valid_flags = 0;
526 529
527 if (kvm_arch_supports_gmem_mmap(kvm))530 if (kvm_arch_supports_gmem_mmap(kvm))
528 valid_flags |= GUEST_MEMFD_FLAG_MMAP;531 valid_flags |= GUEST_MEMFD_FLAG_MMAP |
532 GUEST_MEMFD_FLAG_INIT_SHARED;
529 533
530 if (flags & ~valid_flags)534 if (flags & ~valid_flags)
531 return -EINVAL;535 return -EINVAL;
virt/kvm/kvm_main.c+2−1
@@ -4930,7 +4930,8 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) · 4929 unmodified lines
4930 return 1;4930 return 1;
4931 case KVM_CAP_GUEST_MEMFD_FLAGS:4931 case KVM_CAP_GUEST_MEMFD_FLAGS:
4932 if (!kvm || kvm_arch_supports_gmem_mmap(kvm))4932 if (!kvm || kvm_arch_supports_gmem_mmap(kvm))
4933 return GUEST_MEMFD_FLAG_MMAP;4933 return GUEST_MEMFD_FLAG_MMAP |
4934 GUEST_MEMFD_FLAG_INIT_SHARED;
4934 4935
4935 return 0;4936 return 0;
4936#endif4937#endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t