All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next v2 0/7] More gen_loader fixes
@ 2026-06-01 15:02 Daniel Borkmann
  2026-06-01 15:02 ` [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map Daniel Borkmann
                   ` (7 more replies)
  0 siblings, 8 replies; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh

Follow-up fixes for the signed loader, includes also the recent
sashiko findings.

v1->v2:
  - Fixed up verifier_map_ptr selftest
  - Added patch 1/2/6/7 with a new map-in-map fix and a
    redundant hash_buf memcpy cleanup as well as selftests

Daniel Borkmann (5):
  bpf: Reject exclusive maps as inner maps in map-in-map
  bpf: Drop redundant hash_buf from map_get_hash operation
  libbpf: Skip initial_value override on signed loaders
  libbpf: Skip max_entries override on signed loaders
  selftests/bpf: Test that exclusive maps are rejected in map-in-map

KP Singh (2):
  libbpf: Reject non-exclusive metadata maps in the signed loader
  selftests/bpf: Adjust verifier_map_ptr for the map's excl field

 include/linux/bpf.h                           |  3 +-
 kernel/bpf/arraymap.c                         |  6 +-
 kernel/bpf/map_in_map.c                       |  5 +-
 kernel/bpf/syscall.c                          | 15 ++--
 tools/lib/bpf/gen_loader.c                    | 69 ++++++++++++++-----
 .../selftests/bpf/prog_tests/map_excl.c       | 46 +++++++++++++
 .../selftests/bpf/progs/verifier_map_ptr.c    |  8 ++-
 7 files changed, 121 insertions(+), 31 deletions(-)

-- 
2.43.0


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

* [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
@ 2026-06-01 15:02 ` Daniel Borkmann
  2026-06-01 15:18   ` sashiko-bot
  2026-06-01 15:02 ` [PATCH bpf-next v2 2/7] bpf: Drop redundant hash_buf from map_get_hash operation Daniel Borkmann
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh, sashiko

An exclusive map (created with excl_prog_hash) is bound to a single
program by hash: check_map_prog_compatibility() refuses to load any
program whose digest does not match map->excl_prog_sha. That check
only runs for maps a program references directly, i.e. its used_maps.
A map reached at runtime through a map-of-maps is never in used_maps,
and bpf_map_meta_equal() does not consider excl_prog_sha, so an
exclusive map can be inserted into a non-exclusive outer map and
then looked up and mutated by an unrelated program, bypassing the
exclusivity guarantee.

For the signed loader this defeats the metadata map exclusivity check
added in the signed loader: the cached map->sha[] is validated against
the signed hash while another program on a hostile host rewrites the
frozen map's contents through the outer map.

Fixes: baefdbdf6812 ("bpf: Implement exclusive map creation")
Reported-by: sashiko <sashiko@sashiko.dev>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 kernel/bpf/map_in_map.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
index 645bd30bc9a9..d2cbab4bdf64 100644
--- a/kernel/bpf/map_in_map.c
+++ b/kernel/bpf/map_in_map.c
@@ -20,7 +20,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
 	/* Does not support >1 level map-in-map */
 	if (inner_map->inner_map_meta)
 		return ERR_PTR(-EINVAL);
-
+	if (inner_map->excl_prog_sha)
+		return ERR_PTR(-ENOTSUPP);
 	if (!inner_map->ops->map_meta_equal)
 		return ERR_PTR(-ENOTSUPP);
 
@@ -101,6 +102,8 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
 	inner_map = __bpf_map_get(f);
 	if (IS_ERR(inner_map))
 		return inner_map;
+	if (inner_map->excl_prog_sha)
+		return ERR_PTR(-ENOTSUPP);
 
 	inner_map_meta = map->inner_map_meta;
 	if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map))
-- 
2.43.0


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

* [PATCH bpf-next v2 2/7] bpf: Drop redundant hash_buf from map_get_hash operation
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
  2026-06-01 15:02 ` [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map Daniel Borkmann
@ 2026-06-01 15:02 ` Daniel Borkmann
  2026-06-01 15:29   ` sashiko-bot
  2026-06-01 15:02 ` [PATCH bpf-next v2 3/7] libbpf: Reject non-exclusive metadata maps in the signed loader Daniel Borkmann
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh

bpf_map_get_info_by_fd() is the only caller of the ->map_get_hash
and always invokes it with hash_buf == map->sha and hash_buf_size
of SHA256_DIGEST_SIZE. array_map_get_hash() in turn lets sha256()
write the digest directly into that buffer (map->sha) and then
performs a trailing memcpy(), which evaluates to memcpy(map->sha,
map->sha, 32): a redundant self-copy. The hash_buf_size argument
was never used at all. Simplify this a bit, no functional change.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 include/linux/bpf.h   | 2 +-
 kernel/bpf/arraymap.c | 6 ++----
 kernel/bpf/syscall.c  | 8 +++-----
 3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 1c6863ce89e0..d69cba55f30f 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -111,7 +111,7 @@ struct bpf_map_ops {
 	long (*map_pop_elem)(struct bpf_map *map, void *value);
 	long (*map_peek_elem)(struct bpf_map *map, void *value);
 	void *(*map_lookup_percpu_elem)(struct bpf_map *map, void *key, u32 cpu);
-	int (*map_get_hash)(struct bpf_map *map, u32 hash_buf_size, void *hash_buf);
+	int (*map_get_hash)(struct bpf_map *map);
 
 	/* funcs called by prog_array and perf_event_array map */
 	void *(*map_fd_get_ptr)(struct bpf_map *map, struct file *map_file,
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index dfb2110ab733..e6271a2bf6d6 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -175,14 +175,12 @@ static void *array_map_lookup_elem(struct bpf_map *map, void *key)
 	return array->value + (u64)array->elem_size * (index & array->index_mask);
 }
 
-static int array_map_get_hash(struct bpf_map *map, u32 hash_buf_size,
-			       void *hash_buf)
+static int array_map_get_hash(struct bpf_map *map)
 {
 	struct bpf_array *array = container_of(map, struct bpf_array, map);
 
 	sha256(array->value, (u64)array->elem_size * array->map.max_entries,
-	       hash_buf);
-	memcpy(array->map.sha, hash_buf, sizeof(array->map.sha));
+	       array->map.sha);
 	return 0;
 }
 
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 2aafd2131983..a27fa2b9b405 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -5434,18 +5434,16 @@ static int bpf_map_get_info_by_fd(struct file *file,
 
 		if (!map->ops->map_get_hash)
 			return -EINVAL;
-
-		if (info.hash_size != SHA256_DIGEST_SIZE)
+		if (info.hash_size != sizeof(map->sha))
 			return -EINVAL;
-
 		if (!READ_ONCE(map->frozen))
 			return -EPERM;
 
-		err = map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, map->sha);
+		err = map->ops->map_get_hash(map);
 		if (err != 0)
 			return err;
 
-		if (copy_to_user(uhash, map->sha, SHA256_DIGEST_SIZE) != 0)
+		if (copy_to_user(uhash, map->sha, sizeof(map->sha)) != 0)
 			return -EFAULT;
 	} else if (info.hash_size) {
 		return -EINVAL;
-- 
2.43.0


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

* [PATCH bpf-next v2 3/7] libbpf: Reject non-exclusive metadata maps in the signed loader
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
  2026-06-01 15:02 ` [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map Daniel Borkmann
  2026-06-01 15:02 ` [PATCH bpf-next v2 2/7] bpf: Drop redundant hash_buf from map_get_hash operation Daniel Borkmann
@ 2026-06-01 15:02 ` Daniel Borkmann
  2026-06-01 16:03   ` bot+bpf-ci
  2026-06-01 15:02 ` [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders Daniel Borkmann
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh

From: KP Singh <kpsingh@kernel.org>

The loader verifies map->sha against the metadata hash in its
instructions. map->sha is calculated when BPF_OBJ_GET_INFO_BY_FD is
called on the frozen map.

While the map is frozen, the /signed loader/ must also ensure the map
is exclusive, as, without exclusivity (which a hostile host could just
omit when loading the loader), another BPF program with map access can
mutate the contents afterwards, so the check passes on stale data.

With the extra check as part of the signed loader, it now refuses to
move on with map->sha validation if the host set it up wrongly.

Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing")
Signed-off-by: KP Singh <kpsingh@kernel.org>
Co-developed-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 include/linux/bpf.h        |  1 +
 kernel/bpf/syscall.c       |  7 +++++++
 tools/lib/bpf/gen_loader.c | 17 +++++++++++++++++
 3 files changed, 25 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index d69cba55f30f..1b81bc9e8191 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -296,6 +296,7 @@ struct bpf_map_owner {
 
 struct bpf_map {
 	u8 sha[SHA256_DIGEST_SIZE];
+	u32 excl;
 	const struct bpf_map_ops *ops;
 	struct bpf_map *inner_map_meta;
 #ifdef CONFIG_SECURITY
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index a27fa2b9b405..625a4366fe6d 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1588,6 +1588,13 @@ static int map_create_alloc(union bpf_attr *attr, bpfptr_t uattr, struct bpf_ver
 			err = -EFAULT;
 			goto free_map;
 		}
+
+		/* See libbpf: emit_signature_match() */
+		BUILD_BUG_ON(offsetof(struct bpf_map, excl) != SHA256_DIGEST_SIZE);
+		BUILD_BUG_ON(!__same_type(map->excl, u32));
+		BUILD_BUG_ON(offsetof(struct bpf_map, sha)  != 0);
+		BUILD_BUG_ON(!__same_type(map->sha, u8[SHA256_DIGEST_SIZE]));
+		map->excl = 1;
 	} else if (attr->excl_prog_hash_size) {
 		bpf_log(log, "Invalid excl_prog_hash_size.\n");
 		err = -EINVAL;
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index 3702c5944bc0..66a02039da8c 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -586,6 +586,23 @@ static void emit_signature_match(struct bpf_gen *gen)
 	__s64 off;
 	int i;
 
+	/*
+	 * Reject if the metadata map is not exclusive. Without exclusivity
+	 * the cached map->sha[] verified above can be stale: another BPF
+	 * program with map access could have mutated the contents between
+	 * BPF_OBJ_GET_INFO_BY_FD and loader execution.
+	 */
+	emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
+					 0, 0, 0, 0));
+	emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, SHA256_DIGEST_LENGTH));
+	off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
+	if (is_simm16(off)) {
+		emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
+		emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 1, off));
+	} else {
+		gen->error = -ERANGE;
+	}
+
 	for (i = 0; i < SHA256_DWORD_SIZE; i++) {
 		emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX,
 						 0, 0, 0, 0));
-- 
2.43.0


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

* [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
                   ` (2 preceding siblings ...)
  2026-06-01 15:02 ` [PATCH bpf-next v2 3/7] libbpf: Reject non-exclusive metadata maps in the signed loader Daniel Borkmann
@ 2026-06-01 15:02 ` Daniel Borkmann
  2026-06-01 15:48   ` bot+bpf-ci
  2026-06-01 15:58   ` sashiko-bot
  2026-06-01 15:02 ` [PATCH bpf-next v2 5/7] libbpf: Skip max_entries " Daniel Borkmann
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh

bpf_gen__map_update_elem() emits code that, when the host-supplied
loader ctx provides a non-NULL map_desc[idx].initial_value, overwrites
the blob value with bytes read from the host (bpf_copy_from_user /
bpf_probe_read_kernel) before the BPF_MAP_UPDATE_ELEM that populates
the program's .data/.rodata/.bss maps.

This override runs after emit_signature_match() has validated map->sha[],
and initial_value is part of neither the signed loader instructions nor
the hashed data blob. For a signed loader this lets an untrusted host
substitute global-variable contents into a program whose code carries
a valid signature, thus weakening what the signature attests to.

The blob already contains the signer-provided value (added via add_data()
and covered by the embedded, signed hash), so simply skip emitting the
override for signed loaders (gen_hash). Runtime initialization stays
available for the unsigned light-skeleton path as before. The jump
offsets within the override block are internal to it, so guarding the
whole block leaves them unchanged.

Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 tools/lib/bpf/gen_loader.c | 39 +++++++++++++++++++++++---------------
 1 file changed, 24 insertions(+), 15 deletions(-)

diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index 66a02039da8c..a5d9c7a5261b 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -1187,27 +1187,36 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue,
 	value = add_data(gen, pvalue, value_size);
 	key = add_data(gen, &zero, sizeof(zero));
 
-	/* if (map_desc[map_idx].initial_value) {
+	/*
+	 * if (map_desc[map_idx].initial_value) {
 	 *    if (ctx->flags & BPF_SKEL_KERNEL)
 	 *        bpf_probe_read_kernel(value, value_size, initial_value);
 	 *    else
 	 *        bpf_copy_from_user(value, value_size, initial_value);
 	 * }
+	 *
+	 * The runtime initial_value comes from the host-supplied loader
+	 * ctx and would overwrite the blob value after emit_signature_match()
+	 * has already validated map->sha[]. For a signed loader (gen_hash)
+	 * the attested blob value must be authoritative, so skip the override
+	 * and leave the hashed value in place.
 	 */
-	emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
-			      sizeof(struct bpf_loader_ctx) +
-			      sizeof(struct bpf_map_desc) * map_idx +
-			      offsetof(struct bpf_map_desc, initial_value)));
-	emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
-	emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
-					 0, 0, 0, value));
-	emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
-	emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
-			      offsetof(struct bpf_loader_ctx, flags)));
-	emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
-	emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
-	emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
-	emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
+	if (!OPTS_GET(gen->opts, gen_hash, false)) {
+		emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
+				      sizeof(struct bpf_loader_ctx) +
+				      sizeof(struct bpf_map_desc) * map_idx +
+				      offsetof(struct bpf_map_desc, initial_value)));
+		emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
+		emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
+						 0, 0, 0, value));
+		emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
+		emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
+				      offsetof(struct bpf_loader_ctx, flags)));
+		emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
+		emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
+		emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
+		emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
+	}
 
 	map_update_attr = add_data(gen, &attr, attr_size);
 	pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n",
-- 
2.43.0


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

* [PATCH bpf-next v2 5/7] libbpf: Skip max_entries override on signed loaders
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
                   ` (3 preceding siblings ...)
  2026-06-01 15:02 ` [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders Daniel Borkmann
@ 2026-06-01 15:02 ` Daniel Borkmann
  2026-06-01 15:02 ` [PATCH bpf-next v2 6/7] selftests/bpf: Adjust verifier_map_ptr for the map's excl field Daniel Borkmann
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh

bpf_gen__map_create() lets the host-supplied loader ctx override a
map's max_entries at runtime (map_desc[idx].max_entries, when non-zero).
This is how the light skeleton sizes maps to the target machine, but
it happens after emit_signature_match() and is covered by neither the
signed loader instructions nor the hashed blob.

For a signed loader this means an untrusted host can re-dimension the
program's maps, outside what the signature attests to. Gate the override
on gen_hash so signed loaders use the signer-provided max_entries baked
into the blob.

Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 tools/lib/bpf/gen_loader.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index a5d9c7a5261b..66e13566bc31 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -546,13 +546,22 @@ void bpf_gen__map_create(struct bpf_gen *gen,
 	default:
 		break;
 	}
-	/* conditionally update max_entries */
-	if (map_idx >= 0)
+
+	/*
+	 * Conditionally update max_entries from the host-supplied loader
+	 * ctx. This sizes the map at runtime, but for a signed loader
+	 * (gen_hash) it would let an untrusted host re-dimension the
+	 * program's maps after emit_signature_match(), outside what the
+	 * signature attests to. Keep the signer-provided max_entries
+	 * baked into the blob in that case.
+	 */
+	if (map_idx >= 0 && !OPTS_GET(gen->opts, gen_hash, false))
 		move_ctx2blob(gen, attr_field(map_create_attr, max_entries), 4,
 			      sizeof(struct bpf_loader_ctx) +
 			      sizeof(struct bpf_map_desc) * map_idx +
 			      offsetof(struct bpf_map_desc, max_entries),
 			      true /* check that max_entries != 0 */);
+
 	/* emit MAP_CREATE command */
 	emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size);
 	debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d",
-- 
2.43.0


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

* [PATCH bpf-next v2 6/7] selftests/bpf: Adjust verifier_map_ptr for the map's excl field
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
                   ` (4 preceding siblings ...)
  2026-06-01 15:02 ` [PATCH bpf-next v2 5/7] libbpf: Skip max_entries " Daniel Borkmann
@ 2026-06-01 15:02 ` Daniel Borkmann
  2026-06-01 16:20   ` sashiko-bot
  2026-06-01 15:02 ` [PATCH bpf-next v2 7/7] selftests/bpf: Test that exclusive maps are rejected in map-in-map Daniel Borkmann
  2026-06-02  1:40 ` [PATCH bpf-next v2 0/7] More gen_loader fixes patchwork-bot+netdevbpf
  7 siblings, 1 reply; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh

From: KP Singh <kpsingh@kernel.org>

Adding the u32 excl field at offset 32 of struct bpf_map right after the
sha[SHA256_DIGEST_SIZE] hash shifts the ops pointer from offset 32 to 40.
Therefore, fix up the test case.

  # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_map_ptr
  [...]
  #637/1   verifier_map_ptr/bpf_map_ptr: read with negative offset rejected:OK
  #637/2   verifier_map_ptr/bpf_map_ptr: read with negative offset rejected @unpriv:OK
  #637/3   verifier_map_ptr/bpf_map_ptr: write rejected:OK
  #637/4   verifier_map_ptr/bpf_map_ptr: write rejected @unpriv:OK
  #637/5   verifier_map_ptr/bpf_map_ptr: read non-existent field rejected:OK
  #637/6   verifier_map_ptr/bpf_map_ptr: read non-existent field rejected @unpriv:OK
  #637/7   verifier_map_ptr/bpf_map_ptr: read ops field accepted:OK
  #637/8   verifier_map_ptr/bpf_map_ptr: read ops field accepted @unpriv:OK
  [...]
  Summary: 2/18 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: KP Singh <kpsingh@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 tools/testing/selftests/bpf/progs/verifier_map_ptr.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
index e2767d27d8aa..d8e822d1a8ba 100644
--- a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
+++ b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
@@ -70,13 +70,15 @@ __naked void bpf_map_ptr_write_rejected(void)
 	: __clobber_all);
 }
 
-/* The first element of struct bpf_map is a SHA256 hash of 32 bytes, accessing
- * into this array is valid. The opts field is now at offset 33.
+/*
+ * struct bpf_map starts with the SHA256 hash sha[32] at offset 0 (a readable
+ * byte array), followed by the u32 excl field at offset 32. Reading a u32 at
+ * offset 33 runs past the end of excl and is rejected.
  */
 SEC("socket")
 __description("bpf_map_ptr: read non-existent field rejected")
 __failure
-__msg("cannot access ptr member ops with moff 32 in struct bpf_map with off 33 size 4")
+__msg("access beyond the end of member excl (mend:36) in struct bpf_map with off 33 size 4")
 __failure_unpriv
 __msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
 __flag(BPF_F_ANY_ALIGNMENT)
-- 
2.43.0


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

* [PATCH bpf-next v2 7/7] selftests/bpf: Test that exclusive maps are rejected in map-in-map
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
                   ` (5 preceding siblings ...)
  2026-06-01 15:02 ` [PATCH bpf-next v2 6/7] selftests/bpf: Adjust verifier_map_ptr for the map's excl field Daniel Borkmann
@ 2026-06-01 15:02 ` Daniel Borkmann
  2026-06-02  1:40 ` [PATCH bpf-next v2 0/7] More gen_loader fixes patchwork-bot+netdevbpf
  7 siblings, 0 replies; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:02 UTC (permalink / raw)
  To: alexei.starovoitov; +Cc: bpf, kpsingh

Add a subtest to map_excl that verifies an exclusive map (created with
excl_prog_hash) cannot be used in a map-of-maps, covering both kernel
enforcement points: i) the inner-map template at map-of-maps creation
and, ii) the element inserted into an existing map-of-maps.

  # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t map_excl
  ./test_progs -t map_excl
  [    1.728106] bpf_testmod: loading out-of-tree module taints kernel.
  [    1.730473] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel
  #215/1   map_excl/map_excl_allowed:OK
  #215/2   map_excl/map_excl_denied:OK
  #215/3   map_excl/map_excl_no_map_in_map:OK
  #215     map_excl:OK
  Summary: 1/3 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 .../selftests/bpf/prog_tests/map_excl.c       | 46 +++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/map_excl.c b/tools/testing/selftests/bpf/prog_tests/map_excl.c
index 6bdc6d6de0da..a213dd559aae 100644
--- a/tools/testing/selftests/bpf/prog_tests/map_excl.c
+++ b/tools/testing/selftests/bpf/prog_tests/map_excl.c
@@ -8,6 +8,10 @@
 
 #include "map_excl.skel.h"
 
+#ifndef SHA256_DIGEST_SIZE
+#define SHA256_DIGEST_SIZE	32
+#endif
+
 static void test_map_excl_allowed(void)
 {
 	struct map_excl *skel = map_excl__open();
@@ -45,10 +49,52 @@ static void test_map_excl_denied(void)
 
 }
 
+static void test_map_excl_no_map_in_map(void)
+{
+	__u8 hash[SHA256_DIGEST_SIZE] = {};
+	LIBBPF_OPTS(bpf_map_create_opts, excl_opts,
+		    .excl_prog_hash = hash,
+		    .excl_prog_hash_size = sizeof(hash));
+	LIBBPF_OPTS(bpf_map_create_opts, outer_opts);
+	int excl_fd, tmpl_fd = -1, outer_fd = -1, err;
+	__u32 key = 0;
+
+	excl_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "excl_inner", 4, 4, 1, &excl_opts);
+	if (!ASSERT_OK_FD(excl_fd, "create exclusive map"))
+		return;
+
+	outer_opts.inner_map_fd = excl_fd;
+	err = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS, "outer_from_excl",
+			     4, 4, 1, &outer_opts);
+	if (err >= 0)
+		close(err);
+	ASSERT_EQ(err, -ENOTSUPP, "reject exclusive map as map-in-map template");
+
+	tmpl_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "tmpl", 4, 4, 1, NULL);
+	if (!ASSERT_OK_FD(tmpl_fd, "create inner template"))
+		goto out;
+
+	outer_opts.inner_map_fd = tmpl_fd;
+	outer_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS, "outer", 4, 4, 1, &outer_opts);
+	if (!ASSERT_OK_FD(outer_fd, "create map-of-maps"))
+		goto out;
+
+	err = bpf_map_update_elem(outer_fd, &key, &excl_fd, 0);
+	ASSERT_EQ(err, -ENOTSUPP, "reject exclusive map as map-in-map element");
+out:
+	if (outer_fd >= 0)
+		close(outer_fd);
+	if (tmpl_fd >= 0)
+		close(tmpl_fd);
+	close(excl_fd);
+}
+
 void test_map_excl(void)
 {
 	if (test__start_subtest("map_excl_allowed"))
 		test_map_excl_allowed();
 	if (test__start_subtest("map_excl_denied"))
 		test_map_excl_denied();
+	if (test__start_subtest("map_excl_no_map_in_map"))
+		test_map_excl_no_map_in_map();
 }
-- 
2.43.0


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

* Re: [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map
  2026-06-01 15:02 ` [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map Daniel Borkmann
@ 2026-06-01 15:18   ` sashiko-bot
  2026-06-01 15:40     ` Daniel Borkmann
  0 siblings, 1 reply; 16+ messages in thread
From: sashiko-bot @ 2026-06-01 15:18 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [Critical] BPF iterators bypass map exclusivity (`excl_prog_sha`) and map freezing (`map->frozen`) protections, allowing arbitrary modification of secure maps.
--

commit 94f0db1114c682145008741a58e7ea931a088e25
Author: Daniel Borkmann <daniel@iogearbox.net>

bpf: Reject exclusive maps as inner maps in map-in-map

This commit prevents an exclusive map from being inserted into a
map-of-maps. This addresses an issue where unrelated programs could
bypass exclusivity guarantees by accessing the map at runtime.

> diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
> index 645bd30bc9a9d..d2cbab4bdf644 100644
> --- a/kernel/bpf/map_in_map.c
> +++ b/kernel/bpf/map_in_map.c
> @@ -20,7 +20,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
>  	/* Does not support >1 level map-in-map */
>  	if (inner_map->inner_map_meta)
>  		return ERR_PTR(-EINVAL);
> -
> +	if (inner_map->excl_prog_sha)
> +		return ERR_PTR(-ENOTSUPP);

[Severity: Critical]
This is a pre-existing issue, but while this patch closes the map-in-map
bypass for exclusive maps, does a similar bypass still exist through BPF
iterators?

Looking at bpf_iter_attach_map() in kernel/bpf/map_iter.c, when a BPF map
iterator is attached via BPF_LINK_CREATE:

	map = bpf_map_get_with_uref(linfo->map.map_fd);
	if (IS_ERR(map))
		return PTR_ERR(map);

// [...]

	if (key_acc_size > key_size || value_acc_size > value_size) {
		err = -EACCES;
		goto put_map;
	}

This function retrieves the target map but appears to lack checks against
map->excl_prog_sha, map->frozen, and BPF_F_RDONLY_PROG.

Furthermore, the iterator context defined in bpf_map_elem_reg_info() sets
up the value argument without the MEM_RDONLY flag:

		{ offsetof(struct bpf_iter__bpf_map_elem, value),
		  PTR_TO_BUF | PTR_MAYBE_NULL },

Because MEM_RDONLY is omitted, the verifier permits BPF programs to perform
direct memory writes to ctx->value.

Could this allow a program to locate the FD of an exclusive, frozen map via
its ID, attach a custom BPF map iterator, and arbitrarily overwrite the
contents of the protected map?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601150248.394863-1-daniel@iogearbox.net?part=1

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

* Re: [PATCH bpf-next v2 2/7] bpf: Drop redundant hash_buf from map_get_hash operation
  2026-06-01 15:02 ` [PATCH bpf-next v2 2/7] bpf: Drop redundant hash_buf from map_get_hash operation Daniel Borkmann
@ 2026-06-01 15:29   ` sashiko-bot
  0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-06-01 15:29 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Computing the SHA-256 hash of a large frozen array map without scheduling points can cause a prolonged CPU stall (soft lockup).
--

commit 5b323404e84282b52180b843142a5d52b5becb22
Author: Daniel Borkmann <daniel@iogearbox.net>

bpf: Drop redundant hash_buf from map_get_hash operation

This commit simplifies the map_get_hash operation by dropping the unused
hash_buf_size argument and removing a redundant self-copy in
array_map_get_hash. The bpf_map_get_info_by_fd function is updated to invoke
it without passing the buffer.

> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
> --- a/kernel/bpf/arraymap.c
> +++ b/kernel/bpf/arraymap.c

[ ... ]

> -static int array_map_get_hash(struct bpf_map *map, u32 hash_buf_size,
> -			       void *hash_buf)
> +static int array_map_get_hash(struct bpf_map *map)
>  {
>  	struct bpf_array *array = container_of(map, struct bpf_array, map);
>  
>  	sha256(array->value, (u64)array->elem_size * array->map.max_entries,
> -	       hash_buf);
> -	memcpy(array->map.sha, hash_buf, sizeof(array->map.sha));
> +	       array->map.sha);

[Severity: High]
This isn't a bug introduced by this patch, but could this sha256 call cause
a soft lockup for very large array maps?

BPF array maps can be allocated with very large sizes. Since sha256 processes
the data sequentially without yielding, computing it over a gigabyte-sized
array might take seconds of uninterrupted CPU time.

Because bpf_map_get_info_by_fd unconditionally triggers this computation when
a user requests the map hash, is there a risk that this could be used to
repeatedly stall the CPU, leading to RCU stalls or soft lockup warnings?

>  	return 0;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601150248.394863-1-daniel@iogearbox.net?part=2

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

* Re: [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map
  2026-06-01 15:18   ` sashiko-bot
@ 2026-06-01 15:40     ` Daniel Borkmann
  0 siblings, 0 replies; 16+ messages in thread
From: Daniel Borkmann @ 2026-06-01 15:40 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: bpf

On 6/1/26 5:18 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> 
> Pre-existing issues:
> - [Critical] BPF iterators bypass map exclusivity (`excl_prog_sha`) and map freezing (`map->frozen`) protections, allowing arbitrary modification of secure maps.
> --
> 
> commit 94f0db1114c682145008741a58e7ea931a088e25
> Author: Daniel Borkmann <daniel@iogearbox.net>
> 
> bpf: Reject exclusive maps as inner maps in map-in-map
> 
> This commit prevents an exclusive map from being inserted into a
> map-of-maps. This addresses an issue where unrelated programs could
> bypass exclusivity guarantees by accessing the map at runtime.
> 
>> diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
>> index 645bd30bc9a9d..d2cbab4bdf644 100644
>> --- a/kernel/bpf/map_in_map.c
>> +++ b/kernel/bpf/map_in_map.c
>> @@ -20,7 +20,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
>>   	/* Does not support >1 level map-in-map */
>>   	if (inner_map->inner_map_meta)
>>   		return ERR_PTR(-EINVAL);
>> -
>> +	if (inner_map->excl_prog_sha)
>> +		return ERR_PTR(-ENOTSUPP);
> 
> [Severity: Critical]
> This is a pre-existing issue, but while this patch closes the map-in-map
> bypass for exclusive maps, does a similar bypass still exist through BPF
> iterators?
> 
> Looking at bpf_iter_attach_map() in kernel/bpf/map_iter.c, when a BPF map
> iterator is attached via BPF_LINK_CREATE:
> 
> 	map = bpf_map_get_with_uref(linfo->map.map_fd);
> 	if (IS_ERR(map))
> 		return PTR_ERR(map);
> 
> // [...]
> 
> 	if (key_acc_size > key_size || value_acc_size > value_size) {
> 		err = -EACCES;
> 		goto put_map;
> 	}
> 
> This function retrieves the target map but appears to lack checks against
> map->excl_prog_sha, map->frozen, and BPF_F_RDONLY_PROG.
> 
> Furthermore, the iterator context defined in bpf_map_elem_reg_info() sets
> up the value argument without the MEM_RDONLY flag:
> 
> 		{ offsetof(struct bpf_iter__bpf_map_elem, value),
> 		  PTR_TO_BUF | PTR_MAYBE_NULL },
> 
> Because MEM_RDONLY is omitted, the verifier permits BPF programs to perform
> direct memory writes to ctx->value.
> 
> Could this allow a program to locate the FD of an exclusive, frozen map via
> its ID, attach a custom BPF map iterator, and arbitrarily overwrite the
> contents of the protected map?


Hm, oh well, more things to look into.. if sashiko/others don't find anything
specific with this series, then I'll send patches on top given these are all
independent issues, otherwise I'd fold them into v3.

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

* Re: [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders
  2026-06-01 15:02 ` [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders Daniel Borkmann
@ 2026-06-01 15:48   ` bot+bpf-ci
  2026-06-01 15:58   ` sashiko-bot
  1 sibling, 0 replies; 16+ messages in thread
From: bot+bpf-ci @ 2026-06-01 15:48 UTC (permalink / raw)
  To: daniel, alexei.starovoitov
  Cc: bpf, kpsingh, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

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

> diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
> index 66a02039da8c..a5d9c7a5261b 100644
> --- a/tools/lib/bpf/gen_loader.c
> +++ b/tools/lib/bpf/gen_loader.c
> @@ -1187,27 +1187,36 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue,
>  	value = add_data(gen, pvalue, value_size);
>  	key = add_data(gen, &zero, sizeof(zero));
>
> -	/* if (map_desc[map_idx].initial_value) {
> +	/*
> +	 * if (map_desc[map_idx].initial_value) {
>  	 *    if (ctx->flags & BPF_SKEL_KERNEL)
>  	 *        bpf_probe_read_kernel(value, value_size, initial_value);
>  	 *    else
>  	 *        bpf_copy_from_user(value, value_size, initial_value);
>  	 * }
> +	 *
> +	 * The runtime initial_value comes from the host-supplied loader
> +	 * ctx and would overwrite the blob value after emit_signature_match()
> +	 * has already validated map->sha[]. For a signed loader (gen_hash)
> +	 * the attested blob value must be authoritative, so skip the override
> +	 * and leave the hashed value in place.
>  	 */
> -	emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
> -			      sizeof(struct bpf_loader_ctx) +
> -			      sizeof(struct bpf_map_desc) * map_idx +
> -			      offsetof(struct bpf_map_desc, initial_value)));
> -	emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
> -	emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
> -					 0, 0, 0, value));
> -	emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
> -	emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
> -			      offsetof(struct bpf_loader_ctx, flags)));
> -	emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
> -	emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
> -	emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
> -	emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
> +	if (!OPTS_GET(gen->opts, gen_hash, false)) {
> +		emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_6,
> +				      sizeof(struct bpf_loader_ctx) +
> +				      sizeof(struct bpf_map_desc) * map_idx +
> +				      offsetof(struct bpf_map_desc, initial_value)));
> +		emit(gen, BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 8));
> +		emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX_VALUE,
> +						 0, 0, 0, value));
> +		emit(gen, BPF_MOV64_IMM(BPF_REG_2, value_size));
> +		emit(gen, BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
> +				      offsetof(struct bpf_loader_ctx, flags)));
> +		emit(gen, BPF_JMP_IMM(BPF_JSET, BPF_REG_0, BPF_SKEL_KERNEL, 2));
> +		emit(gen, BPF_EMIT_CALL(BPF_FUNC_copy_from_user));
> +		emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
> +		emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel));
> +	}
>
>  	map_update_attr = add_data(gen, &attr, attr_size);
>  	pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n",

This is a pre-existing issue, but while this patch closes the
map-in-map bypass for exclusive maps, does a similar bypass still
exist through BPF iterators?

Looking at bpf_iter_attach_map() in kernel/bpf/map_iter.c, when a BPF
map iterator is attached via BPF_LINK_CREATE:

	map = bpf_map_get_with_uref(linfo->map.map_fd);
	if (IS_ERR(map))
		return PTR_ERR(map);

	// [...]

	if (key_acc_size > key_size || value_acc_size > value_size) {
		err = -EACCES;
		goto put_map;
	}

This function retrieves the target map but appears to lack checks
against map->excl_prog_sha, map->frozen, and BPF_F_RDONLY_PROG.

Furthermore, the iterator context defined in bpf_map_elem_reg_info()
sets up the value argument without the MEM_RDONLY flag:

	{ offsetof(struct bpf_iter__bpf_map_elem, value),
	  PTR_TO_BUF | PTR_MAYBE_NULL },

Because MEM_RDONLY is omitted, the verifier permits BPF programs to
perform direct memory writes to ctx->value.

Could this allow a program to locate the FD of an exclusive, frozen
map via its ID, attach a custom BPF map iterator, and arbitrarily
overwrite the contents of the protected map?

This concern was raised by sashiko-bot in v2 review comments at:
https://lore.kernel.org/bpf/20260601151810.68BCF1F00893@smtp.kernel.org/


---
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/26764265302

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

* Re: [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders
  2026-06-01 15:02 ` [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders Daniel Borkmann
  2026-06-01 15:48   ` bot+bpf-ci
@ 2026-06-01 15:58   ` sashiko-bot
  1 sibling, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-06-01 15:58 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Integer overflow in `add_data()`'s `roundup(size, 8)` can cause a massive out-of-bounds `memcpy()`, leading to a heap buffer overflow.
--

commit aaabe67208a39bc461c4602ad17bdd3b78f93227
Author: Daniel Borkmann <daniel@iogearbox.net>

    libbpf: Skip initial_value override on signed loaders

    bpf_gen__map_update_elem() emits code that, when the host-supplied
    loader ctx provides a non-NULL map_desc[idx].initial_value, overwrites
    the blob value with bytes read from the host (bpf_copy_from_user /
    bpf_probe_read_kernel) before the BPF_MAP_UPDATE_ELEM that populates
    the program's .data/.rodata/.bss maps. This override runs after
    emit_signature_match() has validated map->sha[], which lets an untrusted
    host substitute global-variable contents. This patch skips emitting the
    override for signed loaders.

> diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
> index 66a02039da8ce..a5d9c7a5261b0 100644
> --- a/tools/lib/bpf/gen_loader.c
> +++ b/tools/lib/bpf/gen_loader.c
> @@ -1187,27 +1187,36 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue,
>  	value = add_data(gen, pvalue, value_size);
>  	key = add_data(gen, &zero, sizeof(zero));

[Severity: High]
This isn't a bug introduced by this patch, but could this call to add_data()
lead to a heap buffer overflow if value_size is extremely large?

Looking at tools/lib/bpf/gen_loader.c:add_data():

static int add_data(struct bpf_gen *gen, const void *data, __u32 size)
{
	__u32 size8 = roundup(size, 8);
...
	if (realloc_data_buf(gen, size8))
		return 0;
	prev = gen->data_cur;
	if (data) {
		memcpy(gen->data_cur, data, size);

If a large size is passed (for example, 0xFFFFFFFC), the roundup(size, 8)
macro would evaluate to size + 7, overflowing the 32-bit unsigned integer
and wrapping around to 0.

This would cause realloc_data_buf() to successfully resize based on the
wrapped size, followed by memcpy() attempting to copy nearly 4GB of data
out of bounds.

Can a maliciously crafted BPF ELF file trigger this integer overflow during
skeleton generation?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601150248.394863-1-daniel@iogearbox.net?part=4

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

* Re: [PATCH bpf-next v2 3/7] libbpf: Reject non-exclusive metadata maps in the signed loader
  2026-06-01 15:02 ` [PATCH bpf-next v2 3/7] libbpf: Reject non-exclusive metadata maps in the signed loader Daniel Borkmann
@ 2026-06-01 16:03   ` bot+bpf-ci
  0 siblings, 0 replies; 16+ messages in thread
From: bot+bpf-ci @ 2026-06-01 16:03 UTC (permalink / raw)
  To: daniel, alexei.starovoitov
  Cc: bpf, kpsingh, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

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

>     libbpf: Reject non-exclusive metadata maps in the signed loader
>
>     The loader verifies map->sha against the metadata hash in its
>     instructions. map->sha is calculated when BPF_OBJ_GET_INFO_BY_FD is
>     called on the frozen map.
>
>     While the map is frozen, the /signed loader/ must also ensure the map
>     is exclusive, as, without exclusivity (which a hostile host could just
>     omit when loading the loader), another BPF program with map access can
>     mutate the contents afterwards, so the check passes on stale data.
>
>     With the extra check as part of the signed loader, it now refuses to
>     move on with map->sha validation if the host set it up wrongly.
>
>     Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing")
>     Signed-off-by: KP Singh <kpsingh@kernel.org>
>     Co-developed-by: Daniel Borkmann <daniel@iogearbox.net>
>     Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

Is the Fixes: tag pointing at the right commit?

The bug being fixed is in emit_signature_match() in
tools/lib/bpf/gen_loader.c, which verifies map->sha without checking map
exclusivity. That function and its map->sha verification loop were
introduced by:

  ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader")

The referenced commit fb2b0e290147 ("libbpf: Update light skeleton for
signing") only touches tools/lib/bpf/skel_internal.h (the light-skeleton
runtime path) and does not introduce the verification code being changed
here.

A sibling fix in the same area, 3c5e2f1a8584 ("libbpf: Skip hash
computation when loader generation failed"), also touches gen_loader.c
and attributes it to ea923080c145.

Should the tag instead be:

  Fixes: ea923080c145 ("libbpf: Embed and verify the metadata hash in the loader")


---
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/26764265302

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

* Re: [PATCH bpf-next v2 6/7] selftests/bpf: Adjust verifier_map_ptr for the map's excl field
  2026-06-01 15:02 ` [PATCH bpf-next v2 6/7] selftests/bpf: Adjust verifier_map_ptr for the map's excl field Daniel Borkmann
@ 2026-06-01 16:20   ` sashiko-bot
  0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-06-01 16:20 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Incomplete and degrading adjustment of verifier_map_ptr tests after struct bpf_map layout change.
--

commit 5b67581177de15e6a8e536ecacc43112b6c44726
Author: KP Singh <kpsingh@kernel.org>

selftests/bpf: Adjust verifier_map_ptr for the map's excl field

Adding the u32 excl field at offset 32 of struct bpf_map right after the
sha[SHA256_DIGEST_SIZE] hash shifts the ops pointer from offset 32 to 40.
This commit attempts to fix up the test case by updating the expected error
messages.

> diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> index e2767d27d8aaf..d8e822d1a8bab 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> @@ -70,13 +70,15 @@ __naked void bpf_map_ptr_write_rejected(void)
>  	: __clobber_all);
>  }
>  
> -/* The first element of struct bpf_map is a SHA256 hash of 32 bytes, accessing
> - * into this array is valid. The opts field is now at offset 33.
> +/*
> + * struct bpf_map starts with the SHA256 hash sha[32] at offset 0 (a readable
> + * byte array), followed by the u32 excl field at offset 32. Reading a u32 at
> + * offset 33 runs past the end of excl and is rejected.
>   */
>  SEC("socket")
>  __description("bpf_map_ptr: read non-existent field rejected")
>  __failure
> -__msg("cannot access ptr member ops with moff 32 in struct bpf_map with off 33 size 4")
> +__msg("access beyond the end of member excl (mend:36) in struct bpf_map with off 33 size 4")
>  __failure_unpriv
>  __msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
>  __flag(BPF_F_ANY_ALIGNMENT)

[Severity: Medium]
Does this adjustment reduce the test suite's coverage?

By only changing the expected error message here,
read_non_existent_field_rejected() now tests for a scalar out-of-bounds
access rather than testing the verifier's rejection of partial pointer
accesses.

Should the offset inside read_non_existent_field_rejected() be updated to 41
instead, to continue testing partial access to the ops pointer (which shifted
to offset 40)?

tools/testing/selftests/bpf/progs/verifier_map_ptr.c:read_non_existent_field_rejected() {
    ...
    r6 = *(u32*)(r1 + 33);
    ...
}

Additionally, ptr_read_ops_field_accepted() explicitly claims to read the
ops field, but still reads from the sha array at offset 0:

tools/testing/selftests/bpf/progs/verifier_map_ptr.c:ptr_read_ops_field_accepted() {
    ...
    r6 = *(u64*)(r1 + 0);
    ...
}

Should this test also be updated to read from offset 40 to properly verify
valid access to the ops pointer?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260601150248.394863-1-daniel@iogearbox.net?part=6

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

* Re: [PATCH bpf-next v2 0/7] More gen_loader fixes
  2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
                   ` (6 preceding siblings ...)
  2026-06-01 15:02 ` [PATCH bpf-next v2 7/7] selftests/bpf: Test that exclusive maps are rejected in map-in-map Daniel Borkmann
@ 2026-06-02  1:40 ` patchwork-bot+netdevbpf
  7 siblings, 0 replies; 16+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-06-02  1:40 UTC (permalink / raw)
  To: Daniel Borkmann; +Cc: alexei.starovoitov, bpf, kpsingh

Hello:

This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Mon,  1 Jun 2026 17:02:41 +0200 you wrote:
> Follow-up fixes for the signed loader, includes also the recent
> sashiko findings.
> 
> v1->v2:
>   - Fixed up verifier_map_ptr selftest
>   - Added patch 1/2/6/7 with a new map-in-map fix and a
>     redundant hash_buf memcpy cleanup as well as selftests
> 
> [...]

Here is the summary with links:
  - [bpf-next,v2,1/7] bpf: Reject exclusive maps as inner maps in map-in-map
    https://git.kernel.org/bpf/bpf-next/c/9a3c3c49c333
  - [bpf-next,v2,2/7] bpf: Drop redundant hash_buf from map_get_hash operation
    https://git.kernel.org/bpf/bpf-next/c/c48c3a7e7d5b
  - [bpf-next,v2,3/7] libbpf: Reject non-exclusive metadata maps in the signed loader
    https://git.kernel.org/bpf/bpf-next/c/0fb6c9ed6493
  - [bpf-next,v2,4/7] libbpf: Skip initial_value override on signed loaders
    https://git.kernel.org/bpf/bpf-next/c/61e084152328
  - [bpf-next,v2,5/7] libbpf: Skip max_entries override on signed loaders
    https://git.kernel.org/bpf/bpf-next/c/60214435b365
  - [bpf-next,v2,6/7] selftests/bpf: Adjust verifier_map_ptr for the map's excl field
    https://git.kernel.org/bpf/bpf-next/c/38498c0ebacd
  - [bpf-next,v2,7/7] selftests/bpf: Test that exclusive maps are rejected in map-in-map
    https://git.kernel.org/bpf/bpf-next/c/32f725458a1a

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

end of thread, other threads:[~2026-06-02  1:40 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-01 15:02 [PATCH bpf-next v2 0/7] More gen_loader fixes Daniel Borkmann
2026-06-01 15:02 ` [PATCH bpf-next v2 1/7] bpf: Reject exclusive maps as inner maps in map-in-map Daniel Borkmann
2026-06-01 15:18   ` sashiko-bot
2026-06-01 15:40     ` Daniel Borkmann
2026-06-01 15:02 ` [PATCH bpf-next v2 2/7] bpf: Drop redundant hash_buf from map_get_hash operation Daniel Borkmann
2026-06-01 15:29   ` sashiko-bot
2026-06-01 15:02 ` [PATCH bpf-next v2 3/7] libbpf: Reject non-exclusive metadata maps in the signed loader Daniel Borkmann
2026-06-01 16:03   ` bot+bpf-ci
2026-06-01 15:02 ` [PATCH bpf-next v2 4/7] libbpf: Skip initial_value override on signed loaders Daniel Borkmann
2026-06-01 15:48   ` bot+bpf-ci
2026-06-01 15:58   ` sashiko-bot
2026-06-01 15:02 ` [PATCH bpf-next v2 5/7] libbpf: Skip max_entries " Daniel Borkmann
2026-06-01 15:02 ` [PATCH bpf-next v2 6/7] selftests/bpf: Adjust verifier_map_ptr for the map's excl field Daniel Borkmann
2026-06-01 16:20   ` sashiko-bot
2026-06-01 15:02 ` [PATCH bpf-next v2 7/7] selftests/bpf: Test that exclusive maps are rejected in map-in-map Daniel Borkmann
2026-06-02  1:40 ` [PATCH bpf-next v2 0/7] More gen_loader fixes patchwork-bot+netdevbpf

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.