Re: [PATCH RESEND] misc/vmw_vmci: bail out earlier on too big queue allocation

From: Arnd Bergmann
Date: Tue Dec 01 2020 - 15:20:51 EST


On Mon, Nov 23, 2020 at 10:01 PM Norbert Slusarek <nslusarek@xxxxxxx> wrote:
>
> From: Norbert Slusarek <nslusarek@xxxxxxx>
> Date: Mon, 23 Nov 2020 21:53:41 +0100
> Subject: [PATCH RESEND] misc/vmw_vmci: bail out earlier on too big queue allocation
>
> For the allocation of a queue pair in qp_host_alloc_queue() an arbitrary value
> can be passed for produce_size, which can lead to miscalculation of memory we'd
> like to allocate in one take. A warning is triggered at __alloc_pages_nodemask()
> in mm/page_alloc.c:4737 which aborts the false allocation, but it results in a
> VMware machine freezing for an infinite time.
>
> Signed-off-by: Norbert Slusarek <nslusarek@xxxxxxx>

Thank you for sending a patch with such a detailed analysis and even
including a test case!

> diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c
> index c49065887e8f..997ff32475b2 100644
> --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c
> +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c
> @@ -526,6 +526,7 @@ static struct vmci_queue *qp_host_alloc_queue(u64 size)
> struct vmci_queue *queue;
> size_t queue_page_size;
> u64 num_pages;
> + unsigned int order;
> const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if));
>
> if (size > SIZE_MAX - PAGE_SIZE)
> @@ -537,6 +538,10 @@ static struct vmci_queue *qp_host_alloc_queue(u64 size)
>
> queue_page_size = num_pages * sizeof(*queue->kernel_if->u.h.page);
>
> + order = get_order(queue_size + queue_page_size);
> + if (order >= MAX_ORDER)
> + return NULL;
> +
> queue = kzalloc(queue_size + queue_page_size, GFP_KERNEL);

Calling kzalloc() with that user-provided size may still not be a great
idea, and MAX_ORDER is probably more than anyone ever needs here
(I don't know what the interface is needed for, but usually it is).

If there is a reasonable upper bound smaller than MAX_ORDER, I
would suggest using that. It might also be helpful to use kvzalloc()/kvfree()
in this case, which can return an arbitrary number of pages
and suffers less from fragmentation.

Note that both kzalloc() and kvzalloc() will fail for much smaller
sizes if the kernel is low on memory, so that might have the same
problem.

Arnd