* [PATCH v2 0/2] gen_loader fixes
@ 2026-05-21 15:22 KP Singh
2026-05-21 15:22 ` [PATCH v2 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
2026-05-21 15:22 ` [PATCH v2 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
0 siblings, 2 replies; 5+ messages in thread
From: KP Singh @ 2026-05-21 15:22 UTC (permalink / raw)
To: bpf, linux-security-module; +Cc: ast, daniel, memxor
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 | 19 +++++++++++++++++--
.../selftests/bpf/progs/verifier_map_ptr.c | 10 ++++++----
3 files changed, 24 insertions(+), 7 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] libbpf: fix off-by-one in emit_signature_match jump offset
2026-05-21 15:22 [PATCH v2 0/2] gen_loader fixes KP Singh
@ 2026-05-21 15:22 ` KP Singh
2026-05-21 16:03 ` bot+bpf-ci
2026-05-21 15:22 ` [PATCH v2 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
1 sibling, 1 reply; 5+ messages in thread
From: KP Singh @ 2026-05-21 15:22 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] 5+ messages in thread
* [PATCH v2 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
2026-05-21 15:22 [PATCH v2 0/2] gen_loader fixes KP Singh
2026-05-21 15:22 ` [PATCH v2 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
@ 2026-05-21 15:22 ` KP Singh
2026-05-21 16:03 ` bot+bpf-ci
1 sibling, 1 reply; 5+ messages in thread
From: KP Singh @ 2026-05-21 15:22 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. Declare excl_prog_sha with __bpf_md_ptr so the
8-byte BPF_LDX_MEM read works on 32-bit kernels.
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 | 16 ++++++++++++++++
.../selftests/bpf/progs/verifier_map_ptr.c | 10 ++++++----
3 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index cd191c5fdb0a..ea9bd24f82c0 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];
+ __bpf_md_ptr(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..fee35c26deb8 100644
--- a/tools/lib/bpf/gen_loader.c
+++ b/tools/lib/bpf/gen_loader.c
@@ -600,6 +600,22 @@ 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..baef2e93cb41 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 (8 bytes via
+ * __bpf_md_ptr), then ops at offset 40. Reading at offset 41 crosses into
+ * the ops 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 ops with moff 40 in struct bpf_map with off 41 size 4")
__failure_unpriv
__msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
__flag(BPF_F_ANY_ALIGNMENT)
@@ -85,7 +87,7 @@ __naked void read_non_existent_field_rejected(void)
asm volatile (" \
r6 = 0; \
r1 = %[map_array_48b] ll; \
- r6 = *(u32*)(r1 + 33); \
+ r6 = *(u32*)(r1 + 41); \
r0 = 1; \
exit; \
" :
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] libbpf: fix off-by-one in emit_signature_match jump offset
2026-05-21 15:22 ` [PATCH v2 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
@ 2026-05-21 16:03 ` bot+bpf-ci
0 siblings, 0 replies; 5+ messages in thread
From: bot+bpf-ci @ 2026-05-21 16:03 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: 2074 bytes --]
> commit 4eb2c1ff205ef1d11b97cef9c13092a8b13ba5e1
> Author: KP Singh <kpsingh@kernel.org>
>
> libbpf: fix off-by-one in emit_signature_match jump offset
>
> 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>
The Fixes: tag points to commit fb2b0e290147 ("libbpf: Update light
skeleton for signing"), which does not exist in this repository. The bug was
actually introduced by commit b3beebbe6154 ("adding ci files"), which added
the gen_loader.c file containing the emit_signature_match function with the
off-by-one error in the jump offset calculation. Should the Fixes: tag be:
Fixes: b3beebbe6154 ("adding ci files")
> 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/26236267798
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader
2026-05-21 15:22 ` [PATCH v2 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
@ 2026-05-21 16:03 ` bot+bpf-ci
0 siblings, 0 replies; 5+ messages in thread
From: bot+bpf-ci @ 2026-05-21 16:03 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: 3438 bytes --]
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 1b28cacc3075..5f1ed0823892 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];
> + __bpf_md_ptr(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;
> };
[ ... ]
> diff --git a/tools/lib/bpf/gen_loader.c b/tools/lib/bpf/gen_loader.c
> index 9478b8f78f26..fee35c26deb8 100644
> --- a/tools/lib/bpf/gen_loader.c
> +++ b/tools/lib/bpf/gen_loader.c
> @@ -600,6 +600,22 @@ 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.
> + */
This isn't a bug, but the multi-line comment opening should follow the
BPF subsystem style with /* on its own line.
> + 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;
> + }
> }
[ ... ]
> diff --git a/tools/testing/selftests/bpf/progs/verifier_map_ptr.c b/tools/testing/selftests/bpf/progs/verifier_map_ptr.c
> index e2767d27d8aa..baef2e93cb41 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 (8 bytes via
> + * __bpf_md_ptr), then ops at offset 40. Reading at offset 41 crosses into
> + * the ops 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 ops with moff 40 in struct bpf_map with off 41 size 4")
> __failure_unpriv
> __msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
> __flag(BPF_F_ANY_ALIGNMENT)
> @@ -85,7 +87,7 @@ __naked void read_non_existent_field_rejected(void)
> asm volatile (" \
> r6 = 0; \
> r1 = %[map_array_48b] ll; \
> - r6 = *(u32*)(r1 + 33); \
> + r6 = *(u32*)(r1 + 41); \
> r0 = 1; \
> exit; \
> " :
---
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/26236267798
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-21 16:03 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-21 15:22 [PATCH v2 0/2] gen_loader fixes KP Singh
2026-05-21 15:22 ` [PATCH v2 1/2] libbpf: fix off-by-one in emit_signature_match jump offset KP Singh
2026-05-21 16:03 ` bot+bpf-ci
2026-05-21 15:22 ` [PATCH v2 2/2] bpf, libbpf: reject non-exclusive metadata maps in the signed loader KP Singh
2026-05-21 16:03 ` bot+bpf-ci
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox