Merge patch series "bpf: Fix OOB in pcpu_init_value and add a test"

xulang <xulang@uniontech.com> says:
====================

Fix OOB read when copying element from a BPF_MAP_TYPE_CGROUP_STORAGE
map to another pcpu map with the same value_size that is not rounded
up to 8 bytes, and add a test case to reproduce the issue.

The root cause is that pcpu_init_value() uses copy_map_value_long() which
rounds up the copy size to 8 bytes, but CGROUP_STORAGE map values are not
8-byte aligned (e.g., 4-byte). This causes a 4-byte OOB read when
the copy is performed.
====================

Link: https://lore.kernel.org/r/7653EEEC2BAB17DF+20260402073948.2185396-1-xulang@uniontech.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-04-12 13:34:41 -07:00
commit fa2942918a
3 changed files with 86 additions and 1 deletions

View File

@ -1056,7 +1056,7 @@ static void pcpu_init_value(struct bpf_htab *htab, void __percpu *pptr,
for_each_possible_cpu(cpu) {
if (cpu == current_cpu)
copy_map_value_long(&htab->map, per_cpu_ptr(pptr, cpu), value);
copy_map_value(&htab->map, per_cpu_ptr(pptr, cpu), value);
else /* Since elem is preallocated, we cannot touch special fields */
zero_map_value(&htab->map, per_cpu_ptr(pptr, cpu));
}

View File

@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <unistd.h>
#include <sys/socket.h>
#include <test_progs.h>
#include "cgroup_helpers.h"
#include "network_helpers.h"
@ -99,3 +101,43 @@ void test_cgroup_storage(void)
close(cgroup_fd);
cleanup_cgroup_environment();
}
void test_cgroup_storage_oob(void)
{
struct cgroup_storage *skel;
int cgroup_fd, sock_fd;
cgroup_fd = cgroup_setup_and_join(TEST_CGROUP);
if (!ASSERT_OK_FD(cgroup_fd, "create cgroup"))
return;
/* Load and attach BPF program */
skel = cgroup_storage__open_and_load();
if (!ASSERT_OK_PTR(skel, "cgroup_storage__open_and_load"))
goto cleanup_cgroup;
skel->links.trigger_oob = bpf_program__attach_cgroup(skel->progs.trigger_oob,
cgroup_fd);
if (!ASSERT_OK_PTR(skel->links.trigger_oob, "attach_cgroup"))
goto cleanup_skel;
/* Create a socket to trigger cgroup/sock_create hook.
* This will execute our BPF program and trigger the OOB read
* if the bug is present (before the fix).
*/
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (!ASSERT_OK_FD(sock_fd, "create socket"))
goto cleanup_skel;
close(sock_fd);
/* If we reach here without a kernel panic or KASAN report,
* the test passes (the fix is working).
*/
cleanup_skel:
cgroup_storage__destroy(skel);
cleanup_cgroup:
close(cgroup_fd);
cleanup_cgroup_environment();
}

View File

@ -21,4 +21,47 @@ int bpf_prog(struct __sk_buff *skb)
return (*counter & 1);
}
/* Maps for OOB test */
struct {
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
__type(key, struct bpf_cgroup_storage_key);
__type(value, __u32); /* 4-byte value - not 8-byte aligned */
} cgroup_storage_oob SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_LRU_PERCPU_HASH);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u32); /* 4-byte value - same as cgroup storage */
} lru_map SEC(".maps");
SEC("cgroup/sock_create")
int trigger_oob(struct bpf_sock *sk)
{
__u32 key = 0;
__u32 *cgroup_val;
__u32 value = 0x12345678;
/* Get cgroup storage value */
cgroup_val = bpf_get_local_storage(&cgroup_storage_oob, 0);
if (!cgroup_val)
return 0;
/* Initialize cgroup storage */
*cgroup_val = value;
/* This triggers the OOB read:
* bpf_map_update_elem() -> htab_map_update_elem() ->
* pcpu_init_value() -> copy_map_value_long() ->
* bpf_obj_memcpy(..., long_memcpy=true) ->
* bpf_long_memcpy(dst, src, round_up(4, 8))
*
* The copy size is rounded up to 8 bytes, but cgroup_val
* points to a 4-byte buffer, causing a 4-byte OOB read.
*/
bpf_map_update_elem(&lru_map, &key, cgroup_val, BPF_ANY);
return 1;
}
char _license[] SEC("license") = "GPL";