Skip to content

Commit fe2bf62

Browse files
committed
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. Fixes: 3d3a04f ("KVM: Allow and advertise support for host mmap() on guest_memfd files") Cc: David Hildenbrand <david@redhat.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> Reviewed-by: Ackerley Tng <ackerleytng@google.com> Tested-by: Ackerley Tng <ackerleytng@google.com> Reviewed-by: David Hildenbrand <david@redhat.com> Link: https://lore.kernel.org/r/20251003232606.4070510-3-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent d2042d8 commit fe2bf62

File tree

5 files changed

+26
-6
lines changed

5 files changed

+26
-6
lines changed

Documentation/virt/kvm/api.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6438,6 +6438,11 @@ specified via KVM_CREATE_GUEST_MEMFD. Currently defined flags:
64386438
============================ ================================================
64396439
GUEST_MEMFD_FLAG_MMAP Enable using mmap() on the guest_memfd file
64406440
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.
64416446
============================ ================================================
64426447

64436448
When the KVM MMU performs a PFN lookup to service a guest fault and the backing

include/uapi/linux/kvm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,8 @@ struct kvm_memory_attributes {
15991599
#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3)
16001600

16011601
#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)
16031604

16041605
struct kvm_create_guest_memfd {
16051606
__u64 size;

tools/testing/selftests/kvm/guest_memfd_test.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,9 @@ static void test_create_guest_memfd_multiple(struct kvm_vm *vm)
239239
close(fd1);
240240
}
241241

242-
static void test_guest_memfd_flags(struct kvm_vm *vm, uint64_t valid_flags)
242+
static void test_guest_memfd_flags(struct kvm_vm *vm)
243243
{
244+
uint64_t valid_flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);
244245
size_t page_size = getpagesize();
245246
uint64_t flag;
246247
int fd;
@@ -274,6 +275,10 @@ static void test_guest_memfd(unsigned long vm_type)
274275
vm = vm_create_barebones_type(vm_type);
275276
flags = vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS);
276277

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+
277282
test_create_guest_memfd_multiple(vm);
278283
test_create_guest_memfd_invalid_sizes(vm, flags, page_size);
279284

@@ -292,7 +297,7 @@ static void test_guest_memfd(unsigned long vm_type)
292297
test_fallocate(fd, page_size, total_size);
293298
test_invalid_punch_hole(fd, page_size, total_size);
294299

295-
test_guest_memfd_flags(vm, flags);
300+
test_guest_memfd_flags(vm);
296301

297302
close(fd);
298303
kvm_vm_free(vm);
@@ -334,9 +339,13 @@ static void test_guest_memfd_guest(void)
334339
TEST_ASSERT(vm_check_cap(vm, KVM_CAP_GUEST_MEMFD_FLAGS) & GUEST_MEMFD_FLAG_MMAP,
335340
"Default VM type should support MMAP, supported flags = 0x%x",
336341
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));
337345

338346
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);
340349
vm_set_user_memory_region2(vm, slot, KVM_MEM_GUEST_MEMFD, gpa, size, NULL, fd, 0);
341350

342351
mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

virt/kvm/guest_memfd.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ static vm_fault_t kvm_gmem_fault_user_mapping(struct vm_fault *vmf)
328328
if (((loff_t)vmf->pgoff << PAGE_SHIFT) >= i_size_read(inode))
329329
return VM_FAULT_SIGBUS;
330330

331+
if (!((u64)inode->i_private & GUEST_MEMFD_FLAG_INIT_SHARED))
332+
return VM_FAULT_SIGBUS;
333+
331334
folio = kvm_gmem_get_folio(inode, vmf->pgoff);
332335
if (IS_ERR(folio)) {
333336
int err = PTR_ERR(folio);
@@ -525,7 +528,8 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args)
525528
u64 valid_flags = 0;
526529

527530
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;
529533

530534
if (flags & ~valid_flags)
531535
return -EINVAL;

virt/kvm/kvm_main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4930,7 +4930,8 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
49304930
return 1;
49314931
case KVM_CAP_GUEST_MEMFD_FLAGS:
49324932
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;
49344935

49354936
return 0;
49364937
#endif

0 commit comments

Comments
 (0)