summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2026-09-30 14:02:04 +0200
committerDaniel Borkmann <daniel@iogearbox.net>2026-09-30 14:02:04 +0200
commit7d2fa34fd3386556dbfa3867368b097cdd9b279e (patch)
treeaca5619dd2e349dffb4a2094438600521ef6c0e9
parentacff58e305175df35985082b0e79103a2497f702 (diff)
parent917cfd1ed5ed295a0ad3df5deb72d2ae9c796dbd (diff)
downloadlinux-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.c2
-rw-r--r--kernel/bpf/verifier.c54
-rw-r--r--tools/testing/selftests/bpf/progs/preempt_lock.c26
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_cfg.c40
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_gotox.c2
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; \