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

Subsystems: bpf [general] (safe dynamic programs and tools), bpf [l7 framework] (sockmap), networking [general], networking [sockets], the rest, xdp (express data path)

STALE1914d

6 messages, 3 authors, 2021-05-17 · open the first message on its own page

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

From: Bui Quang Minh <hidden>
Date: 2021-01-26 11:19:44

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.

Signed-off-by: Bui Quang Minh <redacted>
---
 kernel/bpf/devmap.c | 4 ++--
 net/core/sock_map.c | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c
index f6e9c68afdd4..e849c3e8a49f 100644
--- a/kernel/bpf/devmap.c
+++ b/kernel/bpf/devmap.c
@@ -92,7 +92,7 @@ static struct hlist_head *dev_map_create_hash(unsigned int entries,
 	int i;
 	struct hlist_head *hash;
 
-	hash = bpf_map_area_alloc(entries * sizeof(*hash), numa_node);
+	hash = bpf_map_area_alloc((u64) entries * sizeof(*hash), numa_node);
 	if (hash != NULL)
 		for (i = 0; i < entries; i++)
 			INIT_HLIST_HEAD(&hash[i]);
@@ -143,7 +143,7 @@ static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr)
 
 		spin_lock_init(&dtab->index_lock);
 	} else {
-		dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries *
+		dtab->netdev_map = bpf_map_area_alloc((u64) dtab->map.max_entries *
 						      sizeof(struct bpf_dtab_netdev *),
 						      dtab->map.numa_node);
 		if (!dtab->netdev_map)
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index 64b5ec14ff50..7a42016a981d 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -44,7 +44,7 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr)
 	bpf_map_init_from_attr(&stab->map, attr);
 	raw_spin_lock_init(&stab->lock);
 
-	stab->sks = bpf_map_area_alloc(stab->map.max_entries *
+	stab->sks = bpf_map_area_alloc((u64) stab->map.max_entries *
 				       sizeof(struct sock *),
 				       stab->map.numa_node);
 	if (!stab->sks) {
-- 
2.17.1

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

From: Lorenz Bauer <hidden>
Date: 2021-01-26 11:28:01

On Tue, 26 Jan 2021 at 08:26, Bui Quang Minh [off-list ref] wrote:
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?
* Seems like there are quite a few similar calls scattered around
(cpumap, etc.). Did you audit these as well?
* I'd prefer a calloc style version of bpf_map_area_alloc although
that might conflict with Fixes tag.

Lorenz

-- 
Lorenz Bauer  |  Systems Engineer
6th Floor, County Hall/The Riverside Building, SE1 7PB, UK

www.cloudflare.com

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

From: Bui Quang Minh <hidden>
Date: 2021-01-27 05:10:20

On Tue, Jan 26, 2021 at 09:36:57AM +0000, Lorenz Bauer wrote:
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.
* Seems like there are quite a few similar calls scattered around
(cpumap, etc.). Did you audit these as well?
I spotted another bug after re-auditting. In hashtab, there ares 2 places using
the same calls

	static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
	{
		/* ... snip ... */
		if (htab->n_buckets == 0 ||
		    htab->n_buckets > U32_MAX / sizeof(struct bucket))
			goto free_htab;

		htab->buckets = bpf_map_area_alloc(htab->n_buckets *
						   sizeof(struct bucket),
						   htab->map.numa_node);
	}

This is safe because of the above check.

	static int prealloc_init(struct bpf_htab *htab)
	{
		u32 num_entries = htab->map.max_entries;
		htab->elems = bpf_map_area_alloc(htab->elem_size * num_entries,
						 htab->map.numa_node);
	}

This is not safe since there is no limit check in elem_size.

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.

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.

Overall, I think it is error prone in this pattern, maybe we should use typecasting in all
similar calls or make a comment why we don't use typecasting. As I see typecasting is not so
expensive and we can typecast the sizeof() operand so this change only affect 32-bit
architecture.
* I'd prefer a calloc style version of bpf_map_area_alloc although
that might conflict with Fixes tag.
Yes, I think the calloc style will prevent this kind of integer overflow bug.

Thank you,
Quang Minh.

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

From: Bui Quang Minh <hidden>
Date: 2021-01-27 05:25:18

On Wed, Jan 27, 2021 at 11:23:41AM +0700, Bui Quang Minh wrote:
quoted
* Seems like there are quite a few similar calls scattered around
(cpumap, etc.). Did you audit these as well?
I spotted another bug after re-auditting. In hashtab, there ares 2 places using
the same calls

	static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
	{
		/* ... snip ... */
		if (htab->n_buckets == 0 ||
		    htab->n_buckets > U32_MAX / sizeof(struct bucket))
			goto free_htab;

		htab->buckets = bpf_map_area_alloc(htab->n_buckets *
						   sizeof(struct bucket),
						   htab->map.numa_node);
	}

This is safe because of the above check.

	static int prealloc_init(struct bpf_htab *htab)
	{
		u32 num_entries = htab->map.max_entries;
		htab->elems = bpf_map_area_alloc(htab->elem_size * num_entries,
						 htab->map.numa_node);
	}

This is not safe since there is no limit check in elem_size.
So sorry but I rechecked and saw this bug in hashtab has been fixed with commit
e1868b9e36d0ca

Thank you,
Quang Minh.

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

From: Daniel Borkmann <daniel@iogearbox.net>
Date: 2021-01-28 00:43:26

On 1/27/21 5:23 AM, Bui Quang Minh wrote:
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?
[...]
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.
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").

Thanks,
Daniel

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

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