* [PATCH bpf v3 0/2] gen_loader fixes
@ 2026-05-22 21:53 KP Singh
2026-05-22 21:53 ` [PATCH bpf v3 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: KP Singh @ 2026-05-22 21:53 UTC (permalink / raw)
To: bpf, linux-security-module; +Cc: ast, daniel, memxor
# v2 -> v3
defer fixing 32 bit.
Fix off-by-one in the signed loader's cleanup jump and reject
non-exclusive metadata maps.
Patch 1 fixes a pre-existing bug: the cleanup-label offset in
emit_signature_match() does not account for the MOV insn emitted
before the JMP.
Patch 2 adds an exclusivity check so the loader bails if the
metadata map lacks excl_prog_hash, preventing stale-hash attacks
from concurrent map writers.
KP Singh (2):
libbpf: fix off-by-one in emit_signature_match jump offset
bpf, libbpf: reject non-exclusive metadata maps in the signed loader
include/linux/bpf.h | 2 +-
tools/lib/bpf/gen_loader.c | 20 +++++++++++++++++--
.../selftests/bpf/progs/verifier_map_ptr.c | 8 +++++---
3 files changed, 24 insertions(+), 6 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH bpf v3 1/2] libbpf: fix off-by-one in emit_signature_match jump offset
2026-05-22 21:53 [PATCH bpf v3 0/2] gen_loader fixes KP Singh
@ 2026-05-22 21:53 ` KP Singh
2026-05-22 22:41 ` bot+bpf-ci
2026-05-22 21:53 ` [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
2026-05-23 15:00 ` [PATCH bpf v3 0/2] gen_loader fixes patchwork-bot+netdevbpf
2 siblings, 1 reply; 7+ messages in thread
From: KP Singh @ 2026-05-22 21:53 UTC (permalink / raw)
To: bpf, linux-security-module; +Cc: ast, daniel, memxor, KP Singh
The offset for the cleanup-label jump is computed before the MOV R7
instruction is emitted, but the JMP lands after it. Account for the
extra insn in the offset calculation (-2 instead of -1). Drop the
redundant self-loop in the else branch; gen->error = -ERANGE already
marks the generation as failed.
Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing")
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
tools/lib/bpf/gen_loader.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index cd5c2543f54d..9478b8f78f26 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -592,13 +592,12 @@ static void emit_signature_match(struct bpf_gen *gen)
gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start;
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0));
- off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1;
+ 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_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off));
} else {
gen->error = -ERANGE;
- emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, -1));
}
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
2026-05-22 21:53 [PATCH bpf v3 0/2] gen_loader fixes KP Singh
2026-05-22 21:53 ` [PATCH bpf v3 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
@ 2026-05-22 21:53 ` KP Singh
2026-05-22 22:41 ` bot+bpf-ci
2026-05-23 15:12 ` Alexei Starovoitov
2026-05-23 15:00 ` [PATCH bpf v3 0/2] gen_loader fixes patchwork-bot+netdevbpf
2 siblings, 2 replies; 7+ messages in thread
From: KP Singh @ 2026-05-22 21:53 UTC (permalink / raw)
To: bpf, linux-security-module; +Cc: ast, daniel, memxor, KP Singh
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 loader must also ensure the map is
exclusive, as, without exclusivity, another BPF program with map access
can mutate the contents afterwards, so the check passes on stale data.
Place excl_prog_sha right after sha[] in struct bpf_map and have
gen_loader bail with -EINVAL when it is NULL, via BPF_PSEUDO_MAP_IDX at
fixed offset 32. The 8-byte read of the pointer field limits this to
64-bit kernels; gen_loader needs target pointer size tracking to emit
the right sized read on 32-bit (follow-up).
Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing")
Signed-off-by: KP Singh <kpsingh@kernel.org>
---
include/linux/bpf.h | 2 +-
tools/lib/bpf/gen_loader.c | 17 +++++++++++++++++
.../selftests/bpf/progs/verifier_map_ptr.c | 8 +++++---
3 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index cd191c5fdb0a..11bec73db199 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -295,6 +295,7 @@ struct bpf_map_owner {
struct bpf_map {
u8 sha[SHA256_DIGEST_SIZE];
+ char *excl_prog_sha;
const struct bpf_map_ops *ops;
struct bpf_map *inner_map_meta;
#ifdef CONFIG_SECURITY
@@ -335,7 +336,6 @@ struct bpf_map {
atomic64_t sleepable_refcnt;
s64 __percpu *elem_count;
u64 cookie; /* write-once */
- char *excl_prog_sha;
};
static inline const char *btf_field_type_name(enum btf_field_type type)
diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
index 9478b8f78f26..5077472b9fd2 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -600,6 +600,23 @@ static void emit_signature_match(struct bpf_gen *gen)
gen->error = -ERANGE;
}
}
+
+ /*
+ * 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_DW, BPF_REG_2, BPF_REG_1, SHA256_DWORD_SIZE * sizeof(__u64)));
+ 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_JEQ, BPF_REG_2, 0, off));
+ } else {
+ gen->error = -ERANGE;
+ }
}
void bpf_gen__record_attach_target(struct bpf_gen *gen, const char *attach_name,
diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
index e2767d27d8aa..a89d25ce4f91 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.
+/*
+ * The first 32 bytes of struct bpf_map are the SHA256 hash; accessing into
+ * this array is valid. excl_prog_sha follows at offset 32, reading at
+ * offset 33 crosses into the pointer and must be 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("cannot access ptr member excl_prog_sha with moff 32 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.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf v3 1/2] libbpf: fix off-by-one in emit_signature_match jump offset
2026-05-22 21:53 ` [PATCH bpf v3 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
@ 2026-05-22 22:41 ` bot+bpf-ci
0 siblings, 0 replies; 7+ messages in thread
From: bot+bpf-ci @ 2026-05-22 22:41 UTC (permalink / raw)
To: kpsingh, bpf, linux-security-module
Cc: ast, daniel, memxor, kpsingh, ast, andrii, daniel, martin.lau,
eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1918 bytes --]
> Fixes: fb2b0e290147 ("libbpf: Update light skeleton for signing")
Daniel Borkmann reviewed v2 and pointed out that the Fixes tag
references the wrong commit. The commit fb2b0e290147 only modified
tools/lib/bpf/skel_internal.h and did not touch emit_signature_match.
He suggested using ea923080c145 ("libbpf: Embed and verify the
metadata hash in the loader") instead.
Reference: https://lore.kernel.org/bpf/a9345efb-b691-4fc1-9bad-d571bd324c25@iogearbox.net/
The search for the correct Fixes tag confirmed that fb2b0e290147 did
not introduce the emit_signature_match function or the buggy jump
offset calculation in tools/lib/bpf/gen_loader.c. The true origin
commit could not be determined in this repository's history.
Should the Fixes tag be updated to point to the commit that actually
introduced the emit_signature_match function with the buggy offset
calculation?
> diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
> index cd5c2543f54d..9478b8f78f26 100644
> --- a/tools/lib/bpf/gen_loader.c
> +++ b/tools/lib/bpf/gen_loader.c
> @@ -592,13 +592,12 @@ static void emit_signature_match(struct bpf_gen *gen)
> gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start;
> emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0));
>
> - off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1;
> + 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_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off));
> } else {
> gen->error = -ERANGE;
> - emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, -1));
> }
> }
> }
---
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/26314489328
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
2026-05-22 21:53 ` [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
@ 2026-05-22 22:41 ` bot+bpf-ci
2026-05-23 15:12 ` Alexei Starovoitov
1 sibling, 0 replies; 7+ messages in thread
From: bot+bpf-ci @ 2026-05-22 22:41 UTC (permalink / raw)
To: kpsingh, bpf, linux-security-module
Cc: ast, daniel, memxor, kpsingh, ast, andrii, daniel, martin.lau,
eddyz87, yonghong.song, clm, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3151 bytes --]
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index cd191c5fdb0a..11bec73db199 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -295,6 +295,7 @@ struct bpf_map_owner {
>
> struct bpf_map {
> u8 sha[SHA256_DIGEST_SIZE];
> + char *excl_prog_sha;
Daniel Borkmann suggested adding BUILD_BUG_ON checks to validate the
struct layout assumptions that gen_loader relies on. Specifically:
BUILD_BUG_ON(offsetof(struct bpf_map, sha) != 0);
BUILD_BUG_ON(offsetof(struct bpf_map, excl_prog_sha) != SHA256_DIGEST_SIZE);
BUILD_BUG_ON(sizeof_field(struct bpf_map, excl_prog_sha) != sizeof(__u64));
Would this make the code more robust against future struct layout changes?
> const struct bpf_map_ops *ops;
> struct bpf_map *inner_map_meta;
> #ifdef CONFIG_SECURITY
[ ... ]
> diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
> index 9478b8f78f26..5077472b9fd2 100644
> --- a/tools/lib/bpf/gen_loader.c
> +++ b/tools/lib/bpf/gen_loader.c
> @@ -600,6 +600,23 @@ static void emit_signature_match(struct bpf_gen *gen)
> gen->error = -ERANGE;
> }
> }
> +
> + /*
> + * 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_DW, BPF_REG_2, BPF_REG_1, SHA256_DWORD_SIZE * sizeof(__u64)));
Will this BPF_DW (8-byte) read pass the BPF verifier on 32-bit
architectures?
Sashiko AI flagged this in v2, and you confirmed through testing on a
32-bit kernel that the verifier rejects this with 'cannot access ptr
member excl_prog_sha with moff 0 in struct (anon) with off 0 size 8'.
The issue is that __bpf_md_ptr doesn't emit padding in BTF; the kernel's
BTF represents the pointer member with a size of 4 bytes on 32-bit
systems, while btf_struct_walk() enforces exact size matching.
The commit message acknowledges this: 'The 8-byte read of the pointer
field limits this to 64-bit kernels; gen_loader needs target pointer
size tracking to emit the right sized read on 32-bit (follow-up).'
This means the security check will be silently bypassed on 32-bit
systems until the follow-up is implemented.
Could SHA256_DIGEST_SIZE be used here instead of 'SHA256_DWORD_SIZE *
sizeof(__u64)'? Daniel Borkmann pointed out that SHA256_DWORD_SIZE is
already SHA256_DIGEST_SIZE / sizeof(__u64), so the multiplication
essentially reconstructs SHA256_DIGEST_SIZE.
> + 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_JEQ, BPF_REG_2, 0, off));
> + } else {
> + gen->error = -ERANGE;
> + }
> }
[ ... ]
---
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/26314489328
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf v3 0/2] gen_loader fixes
2026-05-22 21:53 [PATCH bpf v3 0/2] gen_loader fixes KP Singh
2026-05-22 21:53 ` [PATCH bpf v3 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
2026-05-22 21:53 ` [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
@ 2026-05-23 15:00 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 7+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-05-23 15:00 UTC (permalink / raw)
To: KP Singh; +Cc: bpf, linux-security-module, ast, daniel, memxor
Hello:
This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Fri, 22 May 2026 23:53:35 +0200 you wrote:
> # v2 -> v3
>
> defer fixing 32 bit.
>
> Fix off-by-one in the signed loader's cleanup jump and reject
> non-exclusive metadata maps.
>
> [...]
Here is the summary with links:
- [bpf,v3,1/2] libbpf: fix off-by-one in emit_signature_match jump offset
https://git.kernel.org/bpf/bpf/c/7dd62566e0d1
- [bpf,v3,2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
(no matching commit)
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] 7+ messages in thread
* Re: [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
2026-05-22 21:53 ` [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
2026-05-22 22:41 ` bot+bpf-ci
@ 2026-05-23 15:12 ` Alexei Starovoitov
1 sibling, 0 replies; 7+ messages in thread
From: Alexei Starovoitov @ 2026-05-23 15:12 UTC (permalink / raw)
To: KP Singh
Cc: bpf, LSM List, Alexei Starovoitov, Daniel Borkmann,
Kumar Kartikeya Dwivedi
On Fri, May 22, 2026 at 11:53 PM KP Singh <kpsingh@kernel.org> wrote:
>
> 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 loader must also ensure the map is
> exclusive, as, without exclusivity, another BPF program with map access
> can mutate the contents afterwards, so the check passes on stale data.
Hold on. How is this an issue? excl_prog_sha guarantees
that only loader prog can use this map.
Are you saying the same loader prog will use the same map
for the 2nd time. Ok. I still don't see a problem.
> Place excl_prog_sha right after sha[] in struct bpf_map and have
> gen_loader bail with -EINVAL when it is NULL, via BPF_PSEUDO_MAP_IDX at
> fixed offset 32. The 8-byte read of the pointer field limits this to
> 64-bit kernels; gen_loader needs target pointer size tracking to emit
> the right sized read on 32-bit (follow-up).
I don't think we can go from maybe-racy to certainly-broken-on-32-bit.
So only applied patch 1.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-05-23 15:12 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-22 21:53 [PATCH bpf v3 0/2] gen_loader fixes KP Singh
2026-05-22 21:53 ` [PATCH bpf v3 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
2026-05-22 22:41 ` bot+bpf-ci
2026-05-22 21:53 ` [PATCH bpf v3 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
2026-05-22 22:41 ` bot+bpf-ci
2026-05-23 15:12 ` Alexei Starovoitov
2026-05-23 15:00 ` [PATCH bpf v3 0/2] gen_loader fixes patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox