Thread (6 messages) 6 messages, 3 authors, 2021-05-17

Re: [PATCH] bpf: Fix integer overflow in argument calculation for bpf_map_area_alloc

From: Bui Quang Minh <hidden>
Date: 2021-05-17 16:27:59
Also in: bpf, lkml

On 1/28/21 7:41 AM, Daniel Borkmann wrote:
On 1/27/21 5:23 AM, Bui Quang Minh wrote:
quoted
On Tue, Jan 26, 2021 at 09:36:57AM +0000, Lorenz Bauer wrote:
quoted
On Tue, 26 Jan 2021 at 08:26, Bui Quang Minh 
[off-list ref] wrote:
quoted
In 32-bit architecture, the result of sizeof() is a 32-bit integer so
the expression becomes the multiplication between 2 32-bit integer 
which
can potentially leads to integer overflow. As a result,
bpf_map_area_alloc() allocates less memory than needed.

Fix this by casting 1 operand to u64.
Some quick thoughts:
* Should this have a Fixes tag?
Ok, I will add Fixes tag in later version patch.
quoted
* Seems like there are quite a few similar calls scattered around
(cpumap, etc.). Did you audit these as well?
[...]
quoted
In cpumap,

    static struct bpf_map *cpu_map_alloc(union bpf_attr *attr)
    {
        cmap->cpu_map = bpf_map_area_alloc(cmap->map.max_entries *
                           sizeof(struct bpf_cpu_map_entry *),
                           cmap->map.numa_node);
    }

I think this is safe because max_entries is not permitted to be larger 
than NR_CPUS.
Yes.
quoted
In stackmap, there is a place that I'm not very sure about

    static int prealloc_elems_and_freelist(struct bpf_stack_map *smap)
    {
        u32 elem_size = sizeof(struct stack_map_bucket) + 
smap->map.value_size;
        smap->elems = bpf_map_area_alloc(elem_size * 
smap->map.max_entries,
                         smap->map.numa_node);
    }

This is called after another bpf_map_area_alloc in stack_map_alloc(). 
In the first
bpf_map_area_alloc() the argument is calculated in an u64 variable; so 
if in the second
one, there is an integer overflow then the first one must be called 
with size > 4GB. I
think the first one will probably fail (I am not sure about the actual 
limit of vmalloc()),
so the second one might not be called.
I would sanity check this as well. Looks like k*alloc()/v*alloc() call 
sites typically
use array_size() which returns SIZE_MAX on overflow, 610b15c50e86 
("overflow.h: Add
allocation size calculation helpers").
Hi,

I almost forget about this patch, I have checked the bpf_map_area_alloc 
in in stackmap.c and I can see that integer overflow cannot happen in 
this stackmap.c case.

In stack_map_alloc(),

	u64 cost;
	...
	cost = n_buckets * sizeof(struct stack_map_bucket *) + sizeof(*smap);
	cost += n_buckets * (value_size + sizeof(struct stack_map_bucket));
	smap = bpf_map_area_alloc(cost, bpf_map_attr_numa_node(attr)); (1)
	...
	prealloc_elems_and_freelist(smap);

In prealloc_elems_and_freelist(),

	u32 elem_size = sizeof(struct stack_map_bucket) + smap->map.value_size;
	smap->elems = bpf_map_area_alloc(elem_size * smap->map.max_entries, 
smap->map.numa_node); (2)

Argument calculation at (1) is safe. Argument calculation at (2) can 
potentially result in an integer overflow in 32-bit architecture. 
However, if the integer overflow happens, it means argument at (1) must 
be 2**32, which cannot pass the SIZE_MAX check in __bpf_map_area_alloc()

In __bpf_map_area_alloc()

	if (size >= SIZE_MAX)
		return NULL;

So I think the original patch has fixed instances of this bug pattern.

Thank you,
Quang Minh.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help