diff options
| author | Daniel Borkmann <daniel@iogearbox.net> | 2026-09-30 14:02:04 +0200 |
|---|---|---|
| committer | Daniel Borkmann <daniel@iogearbox.net> | 2026-09-30 14:02:04 +0200 |
| commit | 7d2fa34fd3386556dbfa3867368b097cdd9b279e (patch) | |
| tree | aca5619dd2e349dffb4a2094438600521ef6c0e9 | |
| parent | acff58e305175df35985082b0e79103a2497f702 (diff) | |
| parent | 917cfd1ed5ed295a0ad3df5deb72d2ae9c796dbd (diff) | |
| download | linux-next-7d2fa34fd3386556dbfa3867368b097cdd9b279e.tar.gz linux-next-7d2fa34fd3386556dbfa3867368b097cdd9b279e.zip | |
Merge branch 'follow-ups-for-verifier-errors-set'
Kumar Kartikeya Dwivedi says:
====================
Follow ups for verifier errors set
Some follow up changes based on comments from Eduard, Sashiko, and BPF
CI Bot. See commits for details.
Patches 1-4 of v3 were applied to bpf-next. This is the remainder,
rebased on the current bpf-next/master.
Changelog:
----------
v3 -> v4
v3: https://lore.kernel.org/bpf/20260924170646.2366016-1-memxor@gmail.com
* Drop v3 patches 1-4, which are already applied.
* Rebase on the current bpf-next/master.
* State in the Program Structure patch that CO-RE relocations stay where
c26e97721b17 put them and only func_info and line_info validation
moves ahead of the layout checks. (Alexei)
v2 -> v3
v2: https://lore.kernel.org/bpf/20260924092941.3174809-1-memxor@gmail.com
* Rebase on bpf-next/master, which now applies CO-RE relocations before
subprogram discovery, and only move func_info and line_info validation
ahead of the layout checks.
* Drop the truncated final LD_IMM64 relocation guard and its test, as
bpf_check() now rejects that form up front and core_reloc_raw covers it.
v1 -> v2
v1: https://lore.kernel.org/bpf/20260816015746.2632990-1-memxor@gmail.com
* Drop v1 patches 3, 5-7, 11, and 13-14 because they are already present
in bpf-next.
* Use instruction-neutral wording for variable-offset stack accesses
rather than inferring an atomic operation from value_regno == -1. (Eduard)
* Update the variable-offset assertions and cover the retained
uninitialized stack-read diagnostic through the existing CAP_PERFMON-less
read inside the allocated stack. (BPF CI)
* Keep v1 patch 4 unchanged after confirming that the write filter is
reachable for lineage-preserving ALU operations with unchanged
diagnostic snapshots; only its spill arm is impossible. Say so in the
commit message. (Eduard)
* Consider active critical sections only for sleepable programs and let
non-sleepable programs reach the existing no-active-context fallback
instead of adding an early branch. A non-sleepable program can still
hold RCU, preempt, IRQ, or lock state, so the fallback alone would
keep blaming the region. Keep the "non-sleepable prog" description.
(Eduard, BPF CI)
* Select the helper and global-function suggestions by cause, matching
the kfunc site. (BPF CI)
* Split the kfunc coverage into a non-sleepable program case and a
sleepable program inside an RCU read-side critical section. (Eduard)
* Scan subprogram properties with nested loops and drop the partial
recursive-edge details together with their assertion. (Eduard)
* Reject CO-RE relocations targeting a truncated final LD_IMM64 before
applying them, since BTF validation now runs before the subprogram
layout check, and add raw CO-RE coverage for the rejection. Add the
Fixes tag. (BPF CI)
* Rebase on the current bpf-next/master and let the new property scan
also set the callx marker that check_subprogs() collects upstream.
====================
Link: https://patch.msgid.link/20260926133048.2962553-1-memxor@gmail.com
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
| -rw-r--r-- | kernel/bpf/cfg.c | 2 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 54 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/preempt_lock.c | 26 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/verifier_cfg.c | 40 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/verifier_gotox.c | 2 |
5 files changed, 106 insertions, 18 deletions
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c index b0bd9ba951df..d8a579680e5b 100644 --- a/kernel/bpf/cfg.c +++ b/kernel/bpf/cfg.c @@ -393,7 +393,7 @@ subprog_jt(int t, struct bpf_verifier_env *env) if (!subprog->jt) { verbose(env, "no jump tables found for subprog starting at %u\n", subprog_start); bpf_diag_program_structure( - env, subprog_start, "missing jump table", + env, t, "missing jump table", "Make sure subprograms containing gotox instructions are accompanied by jump tables referencing these subprograms.", "No jump table was found for the subprogram that starts at instruction %u.", subprog_start); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1599bac1bac9..b840b3eb9b22 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3107,6 +3107,34 @@ static int add_kfuncs(struct bpf_verifier_env *env) return 0; } +static void find_subprog_properties(struct bpf_verifier_env *env) +{ + struct bpf_subprog_info *subprog = env->subprog_info; + struct bpf_insn *insn = env->prog->insnsi; + int cur_subprog; + + for (cur_subprog = 0; cur_subprog < env->subprog_cnt; cur_subprog++) { + int i; + + for (i = subprog[cur_subprog].start; + i < subprog[cur_subprog + 1].start; i++) { + u8 code = insn[i].code; + + if (code == (BPF_JMP | BPF_CALL) && + insn[i].src_reg == 0 && + insn[i].imm == BPF_FUNC_tail_call) { + subprog[cur_subprog].has_tail_call = true; + subprog[cur_subprog].tail_call_reachable = true; + } + if (BPF_CLASS(code) == BPF_LD && + (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND)) + subprog[cur_subprog].has_ld_abs = true; + if (bpf_is_callx(&insn[i])) + env->has_callx = true; + } + } +} + static int check_subprogs(struct bpf_verifier_env *env) { int i, subprog_start, subprog_end, off, cur_subprog = 0; @@ -3120,17 +3148,6 @@ static int check_subprogs(struct bpf_verifier_env *env) for (i = 0; i < insn_cnt; i++) { u8 code = insn[i].code; - if (code == (BPF_JMP | BPF_CALL) && - insn[i].src_reg == 0 && - insn[i].imm == BPF_FUNC_tail_call) { - subprog[cur_subprog].has_tail_call = true; - subprog[cur_subprog].tail_call_reachable = true; - } - if (BPF_CLASS(code) == BPF_LD && - (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND)) - subprog[cur_subprog].has_ld_abs = true; - if (bpf_is_callx(&insn[i])) - env->has_callx = true; if (BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) goto next; if (BPF_OP(code) == BPF_CALL) @@ -3154,9 +3171,10 @@ static int check_subprogs(struct bpf_verifier_env *env) } next: if (i == subprog_end - 1) { - /* to avoid fall-through from one subprog into another + /* + * To avoid fall-through from one subprog into another, * the last insn of the subprog should be either exit - * or unconditional jump back or bpf_throw call + * or unconditional jump back or bpf_throw call. */ if (code != (BPF_JMP | BPF_EXIT) && code != (BPF_JMP32 | BPF_JA) && @@ -22511,17 +22529,19 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, if (ret < 0) goto skip_full_check; - /* Discover all subprograms before validating their layout and BTF. */ + /* Discover all subprograms and collect the properties needed by BTF validation. */ ret = add_subprogs(env); if (ret < 0) goto skip_full_check; - ret = check_subprogs(env); + find_subprog_properties(env); + + /* Validate BTF before reporting subprogram layout errors. */ + ret = bpf_check_btf_info(env, attr, uattr); if (ret < 0) goto skip_full_check; - /* Validate BTF against the complete subprogram layout. */ - ret = bpf_check_btf_info(env, attr, uattr); + ret = check_subprogs(env); if (ret < 0) goto skip_full_check; diff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c index 81c459435680..955391da326a 100644 --- a/tools/testing/selftests/bpf/progs/preempt_lock.c +++ b/tools/testing/selftests/bpf/progs/preempt_lock.c @@ -6,6 +6,8 @@ #include "bpf_experimental.h" extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr__ign, u64 flags) __weak __ksym; +extern void bpf_rcu_read_lock(void) __ksym; +extern void bpf_rcu_read_unlock(void) __ksym; SEC("?tc") __failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region") @@ -179,6 +181,30 @@ int preempt_sleepable_kfunc(void *ctx) return 0; } +SEC("?fentry/" SYS_PREFIX "sys_getpgid") +__failure __msg("program must be sleepable to call sleepable kfunc bpf_copy_from_user_str") +__msg("cannot be used in non-sleepable program") +int non_sleepable_kfunc(void *ctx) +{ + u32 data; + + bpf_copy_from_user_str(&data, sizeof(data), NULL, 0); + return 0; +} + +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure __msg("kernel func bpf_copy_from_user_str is sleepable within rcu_read_lock region") +__msg("cannot be used in RCU read lock region") +int sleepable_kfunc_in_rcu(void *ctx) +{ + u32 data; + + bpf_rcu_read_lock(); + bpf_copy_from_user_str(&data, sizeof(data), NULL, 0); + bpf_rcu_read_unlock(); + return 0; +} + int __noinline preempt_global_subprog(void) { preempt_balance_subprog(); diff --git a/tools/testing/selftests/bpf/progs/verifier_cfg.c b/tools/testing/selftests/bpf/progs/verifier_cfg.c index 6379dfc9389b..b429fa7a08f5 100644 --- a/tools/testing/selftests/bpf/progs/verifier_cfg.c +++ b/tools/testing/selftests/bpf/progs/verifier_cfg.c @@ -56,6 +56,46 @@ __naked void out_of_range_jump2(void) " ::: __clobber_all); } +static __naked __noinline __used int cross_subprog_target(void) +{ + asm volatile (" \ + r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + +SEC("socket") +__description("jump across subprogram boundary") +__failure __msg("jump out of range from insn 1") +__msg("jump_across_subprog_boundary @ verifier_cfg.c") +__naked void jump_across_subprog_boundary(void) +{ + asm volatile (" \ + call cross_subprog_target; \ + goto +1; \ + exit; \ +" ::: __clobber_all); +} + +static __naked __noinline __used int fallthrough_subprog(void) +{ + asm volatile (" \ + r0 = 0; \ +" ::: __clobber_all); +} + +SEC("socket") +__description("subprogram fallthrough") +__failure __msg("last insn is not an exit or jmp") +__msg("fallthrough_subprog @ verifier_cfg.c") +__naked void subprog_fallthrough(void) +{ + asm volatile (" \ + call fallthrough_subprog; \ + exit; \ +" ::: __clobber_all); +} + SEC("socket") __description("invalid DW LDSX instruction in diagnostics") __failure __msg("BUG_ldx_99") diff --git a/tools/testing/selftests/bpf/progs/verifier_gotox.c b/tools/testing/selftests/bpf/progs/verifier_gotox.c index f5a9878c7b8d..dc7baa0f2458 100644 --- a/tools/testing/selftests/bpf/progs/verifier_gotox.c +++ b/tools/testing/selftests/bpf/progs/verifier_gotox.c @@ -101,9 +101,11 @@ __naked void jump_table_terminal_gotox_subprog(void) */ SEC("socket") __failure __msg("no jump tables found for subprog starting at 0") +__msg(">>> 1 | (0d) gotox r0") __naked void jump_table_no_jump_table(void) { asm volatile (" \ + r0 = 0; \ .8byte %[gotox_r0]; \ r0 = 1; \ exit; \ |
