bpf: Correct Program Structure diagnostic context

Program Structure reports have two attribution gaps. A missing jump
table is reported at the beginning of its subprogram rather than at
the gotox that needs the table, and the subprogram-layout checks run
before func_info and line_info are validated and installed, so their
reports cannot name the source function or line.

Report the missing jump table at the gotox instruction that looks it up,
rather than at the start of its subprogram.

func_info and line_info validation only needs the subprogram boundaries
found by add_subprogs() and the LD_ABS and tail-call properties that
check_subprogs() collects while scanning instructions; it does not
depend on the layout checks themselves. Split the property collection
into its own nested subprogram and instruction scan, together with the
program's callx marker, and run bpf_check_btf_info() before
check_subprogs(). The jump-boundary and fallthrough reports then carry
validated source information without changing either check.

CO-RE relocations are unaffected: commit c26e97721b ("bpf: Apply
CO-RE relocations before subprogram validation") applies them before
add_subprogs(), and they stay there. Only the func_info and line_info
validation moves.

Fixes: a8f4278353 ("bpf: Report Program Structure CFG errors")
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/cf2f420c2b21de440a7dc51b1565c0f06d4b539640ee5c03384e5d77bcfb5686@mail.kernel.org/
Link: https://lore.kernel.org/bpf/20260926133048.2962553-3-memxor@gmail.com
This commit is contained in:
Kumar Kartikeya Dwivedi 2026-09-26 15:30:44 +02:00 committed by Daniel Borkmann
parent 8295510622
commit d91052063b
2 changed files with 38 additions and 18 deletions

View File

@ -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);

View File

@ -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;