Commit 171609f0 authored by Lang Xu's avatar Lang Xu Committed by Alexei Starovoitov
Browse files

selftests/bpf: Add test for cgroup storage OOB read



Add a test case to reproduce the out-of-bounds read issue when copying
from a cgroup storage map to a pcpu map with a value_size not rounded
up to 8 bytes.

The test creates:
1. A CGROUP_STORAGE map with 4-byte value (not 8-byte aligned)
2. A LRU_PERCPU_HASH map with 4-byte value (same size)

When a socket is created in the cgroup, the BPF program triggers
bpf_map_update_elem() which calls copy_map_value_long(). This function
rounds up the copy size to 8 bytes, but the cgroup storage buffer is
only 4 bytes, causing an OOB read (before the fix).

Signed-off-by: default avatarLang Xu <xulang@uniontech.com>
Link: https://lore.kernel.org/r/D63BF0DBFF1EA122+20260402074236.2187154-2-xulang@uniontech.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 576afddf
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
// 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"
@@ -94,3 +96,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();
}
+43 −0
Original line number Diff line number Diff line
@@ -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";