[PATCH bpf] bpf: array: reject max_entries > INT_MAX to prevent signed-iterator overflow

From: meishaoming

Date: Thu Aug 20 2026 - 04:48:15 EST


BPF_MAP_TYPE_PROG_ARRAY (and other array maps) take max_entries as a u32
from userspace via bpf(BPF_MAP_CREATE), but array_map_alloc_check() only
guards value_size > INT_MAX and leaves max_entries unchecked.

When the map is later freed, fd_array_map_free() iterates the ptrs[] array
with a signed int loop variable:

for (i = 0; i < array->map.max_entries; i++)
BUG_ON(array->ptrs[i] != NULL);

On arm64 (and any LP64 arch) int is 32-bit signed, while max_entries is
u32. With max_entries having bit 31 set (e.g. 0xFF00000A, observed from a
syzkaller run), the loop runs past i = 0x7FFFFFFF: i++ wraps to 0x80000000,
which as a signed int is negative. The address computation for
&array->ptrs[i] sign-extends the 32-bit index (sxtw on arm64) into a
64-bit negative offset:

array + sxtw(0x80000000) * 8 + offsetof(bpf_array, ptrs)
= array + 0xFFFFFFFC00000000 + 0x108
-> wraps to an unmapped address, level-1 translation fault, panic

This is reachable today because the existing overflow backstop in
array_map_alloc() lives inside the `if (!bypass_spec_v1)` block. Since
commit 2c78ee898d8f ("bpf: Implement CAP_BPF") renamed the old
`if (unpriv)` guard to `if (!bypass_spec_v1)`, the semantics inverted:
a root caller with CAP_PERFMON (or mitigations=off) takes the bypass
path and skips the -E2BIG check entirely, so the raw attr->max_entries
is stored into map->max_entries unchanged and the ~32 GB vmalloc
(overcommit) region is handed out. The earlier unprivileged-path fix
for max_entries overflow was carried along into the bypass block, so
it does not cover the root path described here.

Reject max_entries > INT_MAX in array_map_alloc_check(), the common entry
point shared by all callers including the bypass path. This mirrors the
existing value_size > INT_MAX guard a few lines above. The cut half of the
u32 range (0x80000000..0xFFFFFFFF) cannot back a usable map today:
- a prog_array at the lower bound already needs ~16 GB of contiguous
virtual memory (and ~32 GB for the 0xFF00000A value seen in the field),
which either fails allocation or drives the system into OOM/softlockup
long before becoming a working map;
- legitimate prog_array usage is bounded by MAX_TAIL_CALL_CNT = 33 and
typical array maps are orders of magnitude below INT_MAX.

No valid BPF use case is affected; the change converts a deferred
whole-system BUG_ON panic (or OOM/softlockup) into an accurate -E2BIG at
creation time.

Fixes: 2c78ee898d8f ("bpf: Implement CAP_BPF")
Signed-off-by: meishaoming <meishaoming@xxxxxxxxxx>
---
kernel/bpf/arraymap.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 248b4818178c..868bc9f66cdb 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -74,6 +74,9 @@ int array_map_alloc_check(union bpf_attr *attr)
/* avoid overflow on round_up(map->value_size) */
if (attr->value_size > INT_MAX)
return -E2BIG;
+ /* avoid signed int iterator overflow in fd_array_map_free() */
+ if (attr->max_entries > INT_MAX)
+ return -E2BIG;
/* percpu map value size is bound by PCPU_MIN_UNIT_SIZE */
if (percpu && round_up(attr->value_size, 8) > PCPU_MIN_UNIT_SIZE)
return -E2BIG;
--
2.50.1 (Apple Git-155)

#/******±¾Óʼþ¼°Æä¸½¼þº¬ÓÐСÃ×¹«Ë¾µÄ±£ÃÜÐÅÏ¢£¬½öÏÞÓÚ·¢Ë͸øÉÏÃæµØÖ·ÖÐÁгöµÄ¸öÈË»òȺ×é¡£½ûÖ¹ÈÎºÎÆäËûÈËÒÔÈκÎÐÎʽʹÓ㨰üÀ¨µ«²»ÏÞÓÚÈ«²¿»ò²¿·ÖµØÐ¹Â¶¡¢¸´ÖÆ¡¢»òÉ¢·¢£©±¾ÓʼþÖеÄÐÅÏ¢¡£Èç¹ûÄú´íÊÕÁ˱¾Óʼþ£¬ÇëÄúÁ¢¼´µç»°»òÓʼþ֪ͨ·¢¼þÈ˲¢É¾³ý±¾Óʼþ£¡ This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#