Skip to content

Commit

Permalink
KVM: guest_mem: Add alignment checks
Browse files Browse the repository at this point in the history
An example for needing the offset alignment check:

Suppose a memslot is configured with

+ guest_phys_addr = 0x400000
+ gmem_offset = 0x201000
+ memory_size = 0x201000
+ Page size = 0x200000 (2M)

The huge page will be allocated at some 2M-aligned boundary, so a
possible PFN would be 0x200. The GFN for an allocation requested by
KVM would be 0x400.

Since GFN of 0x400 translates to offset 0x201000 through the memslot
configuration and the hugepage begins at offset 0x200000 in the file,
the returned page would have a PFN of 0x201, causing

VM_BUG_ON((fault->gfn & mask) != (fault->pfn & mask));

in arch/x86/kvm/mmu/mmu.c to trigger.

Signed-off-by: Ackerley Tng <ackerleytng@google.com>
  • Loading branch information
Ackerley Tng committed Jun 6, 2023
1 parent 52bcfb4 commit cfae00d
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 1 deletion.
25 changes: 25 additions & 0 deletions virt/kvm/guest_mem.c
Expand Up @@ -622,3 +622,28 @@ void kvm_gmem_exit(void)
kern_unmount(kvm_gmem_mnt);
kvm_gmem_mnt = NULL;
}

/**
* Return true if offset and size are page size aligned according to the type of
* backing pages requested.
*
* Offset must be page size aligned because KVM assumes that GFNs and PFNs must
* be at the same index within large pages (see VM_BUG_ON((fault->gfn & mask) !=
* (fault->pfn & mask)); in arch/x86/kvm/mmu/mmu.c).
*
* Size must be aligned because pages are allocated at page granularity. If size
* is smaller than page size, the allocator will be allocating past the end of
* the size of the memslot.
*/
bool kvm_gmem_check_alignment(const struct kvm_userspace_memory_region2 *mem)
{
size_t page_size;

if (mem->flags & KVM_GUEST_MEMFD_HUGE_PMD)
page_size = HPAGE_PMD_SIZE;
else
page_size = PAGE_SIZE;

return (IS_ALIGNED(mem->gmem_offset, page_size) &&
IS_ALIGNED(mem->memory_size, page_size));
}
2 changes: 1 addition & 1 deletion virt/kvm/kvm_main.c
Expand Up @@ -1989,7 +1989,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
if (mem->flags & KVM_MEM_PRIVATE &&
(mem->gmem_offset & (PAGE_SIZE - 1) ||
mem->gmem_offset + mem->memory_size < mem->gmem_offset ||
0 /* TODO: require gfn be aligned with restricted offset */))
!kvm_gmem_check_alignment(mem)))
return -EINVAL;
if (as_id >= kvm_arch_nr_memslot_as_ids(kvm) || id >= KVM_MEM_SLOTS_NUM)
return -EINVAL;
Expand Down
6 changes: 6 additions & 0 deletions virt/kvm/kvm_mm.h
Expand Up @@ -44,6 +44,7 @@ int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *gmem);
int kvm_gmem_bind(struct kvm *kvm, struct kvm_memory_slot *slot,
unsigned int fd, loff_t offset);
void kvm_gmem_unbind(struct kvm_memory_slot *slot);
bool kvm_gmem_check_alignment(const struct kvm_userspace_memory_region2 *mem);
#else
static inline int kvm_gmem_init(void)
{
Expand Down Expand Up @@ -73,6 +74,11 @@ static inline void kvm_gmem_unbind(struct kvm_memory_slot *slot)
{
WARN_ON_ONCE(1);
}

static inline bool kvm_gmem_check_alignment(const struct kvm_userspace_memory_region2 *mem)
{
return false;
}
#endif /* CONFIG_KVM_PRIVATE_MEM */

#endif /* __KVM_MM_H__ */

0 comments on commit cfae00d

Please sign in to comment.