* [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
* 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
* [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 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