BPF List
 help / color / mirror / Atom feed
* [PATCH bpf v3 0/3] bpf: Fix incorrect handling of user flags by percpu map updates
@ 2026-10-03 16:02 Masoud Aghasi
  2026-10-03 16:02 ` [PATCH bpf v3 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Masoud Aghasi @ 2026-10-03 16:02 UTC (permalink / raw)
  To: bpf
  Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
	Masoud Aghasi

For BPF_MAP_TYPE_PERCPU_ARRAY map, bpf_percpu_array_update()
is not considering the possibility of a combination of
(BPF_NOEXIST, BPF_EXIST) flags with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect
in some cases.

For BPF_MAP_TYPE_PERCPU_HASH and BPF_MAP_TYPE_LRU_PERCPU_HASH maps,
htab_map_check_update_flags() and check_flags() are not considering
the possibility of a combination of (BPF_NOEXIST, BPF_EXIST) flags
with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect.

For example, when using (BPF_F_CPU | BPF_EXIST) flag combination
with bpf_map_update_elem() on a BPF_MAP_TYPE_PERCPU_HASH map, the
BPF_EXIST flag does not prevent new insertions as expected.

This series fixes the bug by adding proper flag validations and checks
and adds regression tests.

V3 changes:
- Split the fix into two separate patches for array map and hash maps
- Extend the selftest to cover all percpu map types
- Extend the selftest to cover all applicable flag combinations
- Extend the selftest to cover the unnecessary delete issue of LRU map

V2 changes:
- Fix a BPF_EXIST flag check in __htab_lru_percpu_map_update_elem()
- Add additional test for BPF_MAP_TYPE_LRU_PERCPU_HASH map
- Add check for BPF_EXIST, BPF_NOEXIST combination in percpu array map

Masoud Aghasi (3):
  bpf: Fix incorrect handling of user flags in bpf_percpu_array_update
  bpf: Fix incorrect handling of user flags by percpu hash map updates
  selftests/bpf: add tests for percpu map flags combination

 kernel/bpf/arraymap.c                         |   5 +-
 kernel/bpf/hashtab.c                          |  11 +-
 .../selftests/bpf/prog_tests/percpu_alloc.c   | 159 ++++++++++++++++++
 3 files changed, 169 insertions(+), 6 deletions(-)

-- 
2.47.3


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH bpf v3 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update
  2026-10-03 16:02 [PATCH bpf v3 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
@ 2026-10-03 16:02 ` Masoud Aghasi
  2026-10-03 16:02 ` [PATCH bpf v3 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates Masoud Aghasi
  2026-10-03 16:02 ` [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
  2 siblings, 0 replies; 8+ messages in thread
From: Masoud Aghasi @ 2026-10-03 16:02 UTC (permalink / raw)
  To: bpf
  Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
	Masoud Aghasi

For BPF_MAP_TYPE_PERCPU_ARRAY map, bpf_percpu_array_update()
is not considering the possibility of a combination of
(BPF_NOEXIST, BPF_EXIST) flags with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect
in some cases.

For example, using the (BPF_F_ALL_CPUS | BPF_EXIST) flag combination
with bpf_map_update_elem() results in an incorrect EINVAL error
response, even though the flag combination is valid.

This patch fixes the bug by adding proper flag validations and checks.

Fixes: 8eb76cb03f0f ("bpf: Add BPF_F_CPU and BPF_F_ALL_CPUS flags support for percpu_array maps")
Signed-off-by: Masoud Aghasi <maghasi@disroot.org>
---
 kernel/bpf/arraymap.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 0fe9afd4a591..4edfde6a624c 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -438,7 +438,8 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
 	u32 size;
 	int cpu, off = 0;
 
-	if (unlikely((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS))
+	if (unlikely((map_flags & BPF_EXIST) && (map_flags & BPF_NOEXIST)) ||
+	    unlikely((u32)map_flags & ~(BPF_EXIST | BPF_NOEXIST | BPF_F_CPU | BPF_F_ALL_CPUS)))
 		/* unknown flags */
 		return -EINVAL;
 
@@ -446,7 +447,7 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
 		/* all elements were pre-allocated, cannot insert a new one */
 		return -E2BIG;
 
-	if (unlikely(map_flags == BPF_NOEXIST))
+	if (unlikely(map_flags & BPF_NOEXIST))
 		/* all elements already exist */
 		return -EEXIST;
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH bpf v3 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates
  2026-10-03 16:02 [PATCH bpf v3 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
  2026-10-03 16:02 ` [PATCH bpf v3 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
@ 2026-10-03 16:02 ` Masoud Aghasi
  2026-10-03 16:41   ` bot+bpf-ci
  2026-10-03 16:02 ` [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
  2 siblings, 1 reply; 8+ messages in thread
From: Masoud Aghasi @ 2026-10-03 16:02 UTC (permalink / raw)
  To: bpf
  Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
	Masoud Aghasi

For BPF_MAP_TYPE_PERCPU_HASH and BPF_MAP_TYPE_LRU_PERCPU_HASH maps,
htab_map_check_update_flags() and check_flags() are not considering
the possibility of a combination of (BPF_NOEXIST, BPF_EXIST) flags
with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect.

For example, when using (BPF_F_CPU | BPF_EXIST) flag combination
with bpf_map_update_elem() on a BPF_MAP_TYPE_PERCPU_HASH map, the
BPF_EXIST flag does not prevent new insertions as expected.

This patch fixes the bug by adding proper flag validations and checks.

Fixes: c6936161fd55 ("bpf: Add BPF_F_CPU and BPF_F_ALL_CPUS flags support for percpu_hash and lru_percpu_hash maps")
Signed-off-by: Masoud Aghasi <maghasi@disroot.org>
---
 kernel/bpf/hashtab.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 53c99fe4f176..2106b82894b2 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -1196,11 +1196,11 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
 static int check_flags(struct bpf_htab *htab, struct htab_elem *l_old,
 		       u64 map_flags)
 {
-	if (l_old && (map_flags & ~BPF_F_LOCK) == BPF_NOEXIST)
+	if (l_old && (map_flags & BPF_NOEXIST))
 		/* elem already exists */
 		return -EEXIST;
 
-	if (!l_old && (map_flags & ~BPF_F_LOCK) == BPF_EXIST)
+	if (!l_old && (map_flags & BPF_EXIST))
 		/* elem doesn't exist, cannot update it */
 		return -ENOENT;
 
@@ -1383,9 +1383,12 @@ static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value
 
 static int htab_map_check_update_flags(bool onallcpus, u64 map_flags)
 {
+	if (unlikely((map_flags & BPF_EXIST) && (map_flags & BPF_NOEXIST)))
+		return -EINVAL;
 	if (unlikely(!onallcpus && map_flags > BPF_EXIST))
 		return -EINVAL;
-	if (unlikely(onallcpus && ((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS)))
+	if (unlikely(onallcpus &&
+	    ((u32)map_flags & ~(BPF_EXIST | BPF_NOEXIST | BPF_F_CPU | BPF_F_ALL_CPUS))))
 		return -EINVAL;
 	return 0;
 }
@@ -1483,7 +1486,7 @@ static long __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
 	 * to remove older elem from htab and this removal
 	 * operation will need a bucket lock.
 	 */
-	if (map_flags != BPF_EXIST) {
+	if (!(map_flags & BPF_EXIST)) {
 		l_new = prealloc_lru_pop(htab, key, hash);
 		if (!l_new)
 			return -ENOMEM;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination
  2026-10-03 16:02 [PATCH bpf v3 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
  2026-10-03 16:02 ` [PATCH bpf v3 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
  2026-10-03 16:02 ` [PATCH bpf v3 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates Masoud Aghasi
@ 2026-10-03 16:02 ` Masoud Aghasi
  2026-10-03 16:41   ` bot+bpf-ci
                     ` (2 more replies)
  2 siblings, 3 replies; 8+ messages in thread
From: Masoud Aghasi @ 2026-10-03 16:02 UTC (permalink / raw)
  To: bpf
  Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
	Masoud Aghasi

All possible combinations of (BPF_EXIST, BPF_NOEXIST) and
(BPF_F_ALL_CPUS, BPF_F_CPU) flags are covered for all percpu map types.

As BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE does not support BPF_NOEXIST and
also in order to reduce the amount of code duplicates, I added its
new test scenarios to the existing cpu_flag_percpu_cgroup_storage test.

But for other percpu map types, I added new tests to be able to
exercise all flag combinations and the edge cases such as percpu LRU
unnecessary deletion issue.

Signed-off-by: Masoud Aghasi <maghasi@disroot.org>
---
 .../selftests/bpf/prog_tests/percpu_alloc.c   | 159 ++++++++++++++++++
 1 file changed, 159 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
index 7b4a1e24363b..f50f51c91164 100644
--- a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
+++ b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
@@ -449,6 +449,37 @@ static void test_lru_percpu_hash_cpu_flag_create(void)
 	test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_LRU_PERCPU_HASH, 0);
 }
 
+static void test_percpu_cgroup_storage_flags_combination(struct bpf_map *map, int nr_cpus,
+							  struct bpf_cgroup_storage_key *key)
+{
+	int err;
+	u64 flags = 0;
+	size_t value_sz = sizeof(u32);
+	u32 *values = NULL;
+
+	values = calloc(nr_cpus, value_sz);
+	if (!ASSERT_OK_PTR(values, "calloc values"))
+		return;
+
+	flags = BPF_NOEXIST | BPF_EXIST;
+	err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
+	if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem noexist|exist"))
+		goto out;
+
+	flags = BPF_F_ALL_CPUS | BPF_EXIST;
+	err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
+	if (!ASSERT_OK(err, "bpf_map__update_elem all_cpus|exist"))
+		goto out;
+
+	flags = BPF_F_CPU | BPF_EXIST;
+	err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
+	if (!ASSERT_OK(err, "bpf_map__update_elem cpu|exist"))
+		goto out;
+
+out:
+	free(values);
+}
+
 static void test_percpu_cgroup_storage_cpu_flag(void)
 {
 	struct percpu_alloc_array *skel = NULL;
@@ -489,6 +520,7 @@ static void test_percpu_cgroup_storage_cpu_flag(void)
 		goto out;
 
 	test_percpu_map_op_cpu_flag(map, &key, sizeof(key), 1, nr_cpus, false);
+	test_percpu_cgroup_storage_flags_combination(map, nr_cpus, &key);
 out:
 	bpf_prog_detach2(-1, cgroup, BPF_CGROUP_INET_EGRESS);
 	close(cgroup);
@@ -537,6 +569,127 @@ static void test_hash_cpu_flag(void)
 	test_map_op_cpu_flag(BPF_MAP_TYPE_HASH);
 }
 
+static void test_percpu_map_flags_combination(enum bpf_map_type map_type)
+{
+	size_t value_sz = 8;
+	void *values = NULL;
+	u32 max_entries = 3, key = 0;
+	u64 flags = 0;
+	int err, map_fd, nr_cpus;
+	bool is_hash_map = (map_type == BPF_MAP_TYPE_PERCPU_HASH ||
+			    map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH);
+
+	nr_cpus = libbpf_num_possible_cpus();
+	if (!ASSERT_GT(nr_cpus, 0, "libbpf_num_possible_cpus"))
+		return;
+
+	values = calloc(nr_cpus, value_sz);
+	if (!ASSERT_OK_PTR(values, "calloc values"))
+		return;
+
+	map_fd = bpf_map_create(map_type, "test_flags_combination_map",
+				sizeof(u32), value_sz, max_entries, NULL);
+	if (!ASSERT_GE(map_fd, 0, "bpf_map_create")) {
+		free(values);
+		return;
+	}
+
+	flags = BPF_NOEXIST | BPF_EXIST;
+	err = bpf_map_update_elem(map_fd, &key, values, flags);
+	if (!ASSERT_EQ(err, -EINVAL, "bpf_map_update_elem noexist|exist"))
+		goto out;
+
+	flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
+	key = 0;
+	err = bpf_map_update_elem(map_fd, &key, values, flags);
+	if (is_hash_map) {
+		if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus|noexist"))
+			goto out;
+
+		err = bpf_map_update_elem(map_fd, &key, values, flags);
+		if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
+			goto out;
+	} else {
+		if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
+			goto out;
+	}
+
+	flags = BPF_F_CPU | BPF_NOEXIST;
+	key = 1;
+	err = bpf_map_update_elem(map_fd, &key, values, flags);
+	if (is_hash_map) {
+		if (!ASSERT_OK(err, "bpf_map_update_elem cpu|noexist"))
+			goto out;
+
+		err = bpf_map_update_elem(map_fd, &key, values, flags);
+		if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem cpu|noexist"))
+			goto out;
+	} else {
+		if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem cpu|noexist"))
+			goto out;
+	}
+
+	flags = BPF_F_ALL_CPUS | BPF_EXIST;
+	key = 2;
+	err = bpf_map_update_elem(map_fd, &key, values, flags);
+	if (is_hash_map) {
+		if (!ASSERT_EQ(err, -ENOENT, "bpf_map_update_elem all_cpus|exist"))
+			goto out;
+	} else {
+		if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus|exist"))
+			goto out;
+	}
+
+	flags = BPF_F_CPU | BPF_EXIST;
+	err = bpf_map_update_elem(map_fd, &key, values, flags);
+	if (is_hash_map) {
+		if (!ASSERT_EQ(err, -ENOENT, "bpf_map_update_elem cpu|exist"))
+			goto out;
+	} else {
+		if (!ASSERT_OK(err, "bpf_map_update_elem cpu|exist"))
+			goto out;
+	}
+
+	if (map_type != BPF_MAP_TYPE_LRU_PERCPU_HASH)
+		goto out;
+
+	/* Percpu LRU hash map should not delete old entries unnecessarily */
+	flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
+	key = 2;
+	err = bpf_map_update_elem(map_fd, &key, values, flags);
+	if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
+		goto out;
+
+	flags = BPF_F_ALL_CPUS | BPF_EXIST;
+	err = bpf_map_update_elem(map_fd, &key, values, flags);
+	if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
+		goto out;
+
+	key = 0;
+	err = bpf_map_lookup_elem(map_fd, &key, values);
+	if (!ASSERT_OK(err, "bpf_map_lookup_elem unnecessary_deletion"))
+		goto out;
+
+out:
+	close(map_fd);
+	free(values);
+}
+
+static void test_percpu_hash_flags_combination(void)
+{
+	test_percpu_map_flags_combination(BPF_MAP_TYPE_PERCPU_HASH);
+}
+
+static void test_lru_percpu_hash_flags_combination(void)
+{
+	test_percpu_map_flags_combination(BPF_MAP_TYPE_LRU_PERCPU_HASH);
+}
+
+static void test_percpu_array_flags_combination(void)
+{
+	test_percpu_map_flags_combination(BPF_MAP_TYPE_PERCPU_ARRAY);
+}
+
 void test_percpu_alloc(void)
 {
 	if (test__start_subtest("array"))
@@ -565,4 +718,10 @@ void test_percpu_alloc(void)
 		test_array_cpu_flag();
 	if (test__start_subtest("cpu_flag_hash"))
 		test_hash_cpu_flag();
+	if (test__start_subtest("cpu_flag_combination_percpu_hash"))
+		test_percpu_hash_flags_combination();
+	if (test__start_subtest("cpu_flag_combination_lru_percpu_hash"))
+		test_lru_percpu_hash_flags_combination();
+	if (test__start_subtest("cpu_flag_combination_percpu_array"))
+		test_percpu_array_flags_combination();
 }
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination
  2026-10-03 16:02 ` [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
@ 2026-10-03 16:41   ` bot+bpf-ci
  2026-10-03 17:56   ` sashiko-bot
  2026-10-04  9:39   ` Masoud Aghasi
  2 siblings, 0 replies; 8+ messages in thread
From: bot+bpf-ci @ 2026-10-03 16:41 UTC (permalink / raw)
  To: maghasi, bpf
  Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang, maghasi,
	ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
	ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 5990 bytes --]

> selftests/bpf: add tests for percpu map flags combination

TL;DR:
- Cgroup storage NOEXIST|EXIST test likely never reaches the kernel: libbpf
  rejects the undersized value buffer, so it passes trivially
- LRU percpu hash test may fail spuriously if the thread migrates CPUs; pin it
  to one CPU

> diff --git a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> index 7b4a1e24363b..f50f51c91164 100644
> --- a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> +++ b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c

[ ... ]

> @@ -449,6 +449,37 @@ static void test_lru_percpu_hash_cpu_flag_create(void)

[ ... ]

> +static void test_percpu_cgroup_storage_flags_combination(struct bpf_map *map, int nr_cpus,
> +							  struct bpf_cgroup_storage_key *key)
> +{
> +	int err;
> +	u64 flags = 0;
> +	size_t value_sz = sizeof(u32);
> +	u32 *values = NULL;
> +
> +	values = calloc(nr_cpus, value_sz);
> +	if (!ASSERT_OK_PTR(values, "calloc values"))
> +		return;
> +
> +	flags = BPF_NOEXIST | BPF_EXIST;
> +	err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
> +	if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem noexist|exist"))
> +		goto out;

Does this BPF_NOEXIST | BPF_EXIST check ever reach the kernel?

bpf_map__update_elem() calls validate_map_op() first, and for a per-CPU map
without BPF_F_CPU or BPF_F_ALL_CPUS that function requires:

    if (value_sz != num_cpu * elem_sz) {
        pr_warn("... unexpected value size ...");
        return -EINVAL;
    }

Here value_sz is sizeof(u32) and elem_sz is roundup(value_size, 8) = 8, so
4 != 8 * nr_cpus always holds.  libbpf returns -EINVAL and the bpf() syscall
is never made, so ASSERT_EQ(err, -EINVAL, ...) passes regardless of what
bpf_percpu_cgroup_storage_update() does with BPF_NOEXIST | BPF_EXIST.

Would a value of roundup(value_size, 8) * nr_cpus bytes be needed here,
either passed as value_sz to bpf_map__update_elem() or used with
bpf_map_update_elem() on bpf_map__fd(map)?  The current
calloc(nr_cpus, sizeof(u32)) buffer is only half that size.

The commit message says all flag combinations are covered for all percpu map
types.  For cgroup storage, the NOEXIST | EXIST case ends up being checked
only by libbpf, and NOEXIST combined with BPF_F_CPU or BPF_F_ALL_CPUS is not
tested.

[ ... ]

> @@ -537,6 +569,127 @@ static void test_hash_cpu_flag(void)

[ ... ]

> +static void test_percpu_map_flags_combination(enum bpf_map_type map_type)
> +{
> +	size_t value_sz = 8;
> +	void *values = NULL;
> +	u32 max_entries = 3, key = 0;

[ ... ]

> +	map_fd = bpf_map_create(map_type, "test_flags_combination_map",
> +				sizeof(u32), value_sz, max_entries, NULL);
> +	if (!ASSERT_GE(map_fd, 0, "bpf_map_create")) {
> +		free(values);
> +		return;
> +	}

[ ... ]

> +	/* Percpu LRU hash map should not delete old entries unnecessarily */
> +	flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
> +	key = 2;
> +	err = bpf_map_update_elem(map_fd, &key, values, flags);
> +	if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
> +		goto out;
> +
> +	flags = BPF_F_ALL_CPUS | BPF_EXIST;
> +	err = bpf_map_update_elem(map_fd, &key, values, flags);
> +	if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
> +		goto out;
> +
> +	key = 0;
> +	err = bpf_map_lookup_elem(map_fd, &key, values);
> +	if (!ASSERT_OK(err, "bpf_map_lookup_elem unnecessary_deletion"))
> +		goto out;

Can this LRU check fail on a correct kernel if the test thread migrates to
another CPU partway through?

The map is created with max_entries = 3 and no BPF_F_NO_COMMON_LRU, so it
uses the common LRU with per-CPU local free lists.
bpf_common_lru_populate() sets lru->target_free to
clamp((3 / num_possible_cpus()) / 2, 1, LOCAL_FREE_TARGET) = 1, and the test
needs all three nodes (keys 0, 1 and 2), so there is no spare node.

A node popped by a BPF_NOEXIST update that fails with -EEXIST is returned to
the free list of the CPU that popped it (bpf_common_lru_push_free() uses
node->cpu).  If a later update runs on a different CPU,
bpf_lru_list_pop_free_to_local() finds the global free list empty and calls
__bpf_lru_list_shrink(), which evicts the oldest unreferenced entry through
htab_lru_map_delete_node() instead of using the node left on the other CPU.

With the percpu hash flags fix applied (326efbe72929), one possible
sequence is:

  ALL_CPUS | NOEXIST on key 0, on CPU A:
    A pulls node X1 from the global free list; key 0 = X1

  second ALL_CPUS | NOEXIST on key 0, on CPU A:
    X1 is flushed to inactive and A pulls X2; the update returns -EEXIST
    and X2 goes back to A's local free list

  the thread migrates to CPU B

  CPU | NOEXIST on key 1, on CPU B:
    B pulls X3, which empties the global free list; key 1 = X3

  second CPU | NOEXIST on key 1, on CPU B:
    B's local free list and the global free list are both empty, so
    __bpf_lru_list_shrink_inactive() evicts the inactive tail X1 (key 0);
    the update correctly returns -EEXIST

  key 2 is inserted and the following BPF_EXIST update pops no node

The final bpf_map_lookup_elem() for key 0 then returns -ENOENT, and
ASSERT_OK(err, "bpf_map_lookup_elem unnecessary_deletion") fails.  Other
placements, for example the key 2 insert back on CPU A, also evict key 0.

test_progs calls reset_affinity() before every subtest, so this subtest runs
unpinned, and on a multi-CPU runner the result depends on scheduler
placement.  A failure here would look like the unnecessary deletion kernel
bug the test is meant to catch.

Existing LRU selftests pin the thread to one CPU (sched_next_online() in
test_lru_map.c, pthread_setaffinity_np() in prog_tests/htab_update.c).
Should this subtest pin itself to a single CPU as well?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/37135877031

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf v3 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates
  2026-10-03 16:02 ` [PATCH bpf v3 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates Masoud Aghasi
@ 2026-10-03 16:41   ` bot+bpf-ci
  0 siblings, 0 replies; 8+ messages in thread
From: bot+bpf-ci @ 2026-10-03 16:41 UTC (permalink / raw)
  To: maghasi, bpf
  Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang, maghasi,
	ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
	ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2098 bytes --]

> bpf: Fix incorrect handling of user flags by percpu hash map updates

TL;DR: Commit message only: it should list the user-visible changes
(BPF_F_ALL_CPUS with EXIST/NOEXIST now accepted, EXIST|NOEXIST now -EINVAL),
not just "proper flag validations".

>     bpf: Fix incorrect handling of user flags by percpu hash map updates
>
>     For BPF_MAP_TYPE_PERCPU_HASH and BPF_MAP_TYPE_LRU_PERCPU_HASH maps,
>     htab_map_check_update_flags() and check_flags() are not considering
>     the possibility of a combination of (BPF_NOEXIST, BPF_EXIST) flags
>     with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
>     This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect.
>
>     For example, when using (BPF_F_CPU | BPF_EXIST) flag combination
>     with bpf_map_update_elem() on a BPF_MAP_TYPE_PERCPU_HASH map, the
>     BPF_EXIST flag does not prevent new insertions as expected.
>
>     This patch fixes the bug by adding proper flag validations and checks.
>
>     Fixes: c6936161fd55 ("bpf: Add BPF_F_CPU and BPF_F_ALL_CPUS flags support for percpu_hash and lru_percpu_hash maps")
>     Signed-off-by: Masoud Aghasi <maghasi@disroot.org>

This isn't a bug, but the changelog says the flags "lose their effect" with
BPF_F_CPU/BPF_F_ALL_CPUS and that the fix adds "proper flag validations and
checks".  That is only true of BPF_F_CPU.

Before this patch, htab_map_check_update_flags() rejected
BPF_F_ALL_CPUS | BPF_EXIST and BPF_F_ALL_CPUS | BPF_NOEXIST with -EINVAL,
because (u32)map_flags was greater than BPF_F_ALL_CPUS.  After the patch
those combinations are accepted.

The patch also starts returning -EINVAL for BPF_EXIST | BPF_NOEXIST, with or
without BPF_F_CPU, which was previously accepted after c6936161fd55.

Could the changelog briefly list these user-visible changes, rather than just
saying "adding proper flag validations and checks"?


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/37135877031

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination
  2026-10-03 16:02 ` [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
  2026-10-03 16:41   ` bot+bpf-ci
@ 2026-10-03 17:56   ` sashiko-bot
  2026-10-04  9:39   ` Masoud Aghasi
  2 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-10-03 17:56 UTC (permalink / raw)
  To: Masoud Aghasi; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] bpf: test falsely passes via libbpf size validation masking buffer under-allocation
--

commit 1b911e2bbe76c9b313409a279ec05f4a4e42cb74
Author: Masoud Aghasi <maghasi@disroot.org>

selftests/bpf: add tests for percpu map flags combination

This commit adds tests for all possible combinations of (BPF_EXIST,
BPF_NOEXIST) and (BPF_F_ALL_CPUS, BPF_F_CPU) flags across percpu map types.
It also merges testing of percpu cgroup storage into the existing
cpu_flag_percpu_cgroup_storage test.

> diff --git a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> index 7b4a1e24363b5..f50f51c91164b 100644
> --- a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> +++ b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> @@ -449,6 +449,37 @@ static void test_lru_percpu_hash_cpu_flag_create(void)
>  	test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_LRU_PERCPU_HASH, 0);
>  }
>  
> +static void test_percpu_cgroup_storage_flags_combination(struct bpf_map *map, int nr_cpus,
> +							  struct bpf_cgroup_storage_key *key)
> +{
> +	int err;
> +	u64 flags = 0;
> +	size_t value_sz = sizeof(u32);
> +	u32 *values = NULL;
> +
> +	values = calloc(nr_cpus, value_sz);

[Severity: Medium]
The values array is only allocated 4 * nr_cpus bytes, whereas a valid percpu
map update requires 8 * nr_cpus bytes. If libbpf's size check were bypassed
(e.g., by directly using the bpf() syscall like the other tests do), would
this buffer under-allocation result in an out-of-bounds read when the kernel
attempts to copy 8 * nr_cpus bytes?

> +	if (!ASSERT_OK_PTR(values, "calloc values"))
> +		return;
> +
> +	flags = BPF_NOEXIST | BPF_EXIST;
> +	err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
> +	if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem noexist|exist"))
> +		goto out;

[Severity: Medium]
Does this test incorrectly pass because libbpf intercepts the invalid value_sz,
returning -EINVAL before reaching the kernel? 

validate_map_op() in libbpf expects value_sz to be nr_cpus * 8 because neither
BPF_F_CPU nor BPF_F_ALL_CPUS is set. This seems to mask the fact that the
kernel's map_update_elem syscall is never actually exercised for this specific
flag combination.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261003160213.2641506-1-maghasi@disroot.org?part=3

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination
  2026-10-03 16:02 ` [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
  2026-10-03 16:41   ` bot+bpf-ci
  2026-10-03 17:56   ` sashiko-bot
@ 2026-10-04  9:39   ` Masoud Aghasi
  2 siblings, 0 replies; 8+ messages in thread
From: Masoud Aghasi @ 2026-10-04  9:39 UTC (permalink / raw)
  To: bpf
  Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang

I will add fixes for AI concerns in v4.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-04  9:40 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-03 16:02 [PATCH bpf v3 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
2026-10-03 16:02 ` [PATCH bpf v3 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
2026-10-03 16:02 ` [PATCH bpf v3 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates Masoud Aghasi
2026-10-03 16:41   ` bot+bpf-ci
2026-10-03 16:02 ` [PATCH bpf v3 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
2026-10-03 16:41   ` bot+bpf-ci
2026-10-03 17:56   ` sashiko-bot
2026-10-04  9:39   ` Masoud Aghasi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox