Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

KVM: SEV: Use kvzalloc_objs() when pinning userpages

Use kvzalloc_objs() instead of sev_pin_memory()'s open coded (rough)
equivalent to harden the code and

Note! This sanity check in __kvmalloc_node_noprof()

/* Don't even allow crazy sizes */
if (unlikely(size > INT_MAX)) {
WARN_ON_ONCE(!(flags & __GFP_NOWARN));
return NULL;
}

will artificially limit the maximum size of any single pinned region to
just under 1TiB. While there do appear to be providers that support SEV
VMs with more than 1TiB of _total_ memory, it's unlikely any KVM-based
providers pin 1TiB in a single request.

Allocate with NOWARN so that fuzzers can't trip the WARN_ON_ONCE() when
they inevitably run on systems with copious amounts of RAM, i.e. when they
can get by KVM's "total_npages > totalram_pages()" restriction.

Note #2, KVM's usage of vmalloc()+kmalloc() instead of kvmalloc() predates
commit 7661809d493b ("mm: don't allow oversized kvmalloc() calls") by 4+
years (see commit 89c505809052 ("KVM: SVM: Add support for
KVM_SEV_LAUNCH_UPDATE_DATA command"). I.e. the open coded behavior wasn't
intended to avoid the aforementioned sanity check. The implementation
appears to be pure oversight at the time the code was written, as it showed
up in v3[1] of the early RFCs, whereas as v2[2] simply used kmalloc().

Cc: Liam Merwick <liam.merwick@oracle.com>
Link: https://lore.kernel.org/all/20170724200303.12197-17-brijesh.singh@amd.com [1]
Link: https://lore.kernel.org/all/148846786714.2349.17724971671841396908.stgit__25299.4950431914$1488470940$gmane$org@brijesh-build-machine [2]
Reviewed-by: Liam Merwick <liam.merwick@oracle.com>
Tested-by: Liam Merwick <liam.merwick@oracle.com>
Link: https://patch.msgid.link/20260313003302.3136111-6-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>

+9 -11
+9 -11
arch/x86/kvm/svm/sev.c
··· 678 678 unsigned int flags) 679 679 { 680 680 struct kvm_sev_info *sev = to_kvm_sev_info(kvm); 681 - unsigned long npages, size; 682 - int npinned; 683 - unsigned long total_npages, lock_limit; 681 + unsigned long npages, total_npages, lock_limit; 684 682 struct page **pages; 685 - int ret; 683 + int npinned, ret; 686 684 687 685 lockdep_assert_held(&kvm->lock); 688 686 ··· 707 709 return ERR_PTR(-ENOMEM); 708 710 } 709 711 710 - /* Avoid using vmalloc for smaller buffers. */ 711 - size = npages * sizeof(struct page *); 712 - if (size > PAGE_SIZE) 713 - pages = __vmalloc(size, GFP_KERNEL_ACCOUNT); 714 - else 715 - pages = kmalloc(size, GFP_KERNEL_ACCOUNT); 716 - 712 + /* 713 + * Don't WARN if the kernel (rightly) thinks the total size is absurd, 714 + * i.e. rely on the kernel to reject outrageous range sizes. The above 715 + * check on the number of pages is purely to avoid truncation as 716 + * pin_user_pages_fast() takes the number of pages as a 32-bit int. 717 + */ 718 + pages = kvzalloc_objs(*pages, npages, GFP_KERNEL_ACCOUNT | __GFP_NOWARN); 717 719 if (!pages) 718 720 return ERR_PTR(-ENOMEM); 719 721