[PATCH bpf-next v1 10/14] bpf: Correct Program Structure diagnostic context
Kumar Kartikeya Dwivedi <[email protected]>
| Newsgroups | org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
Program Structure reports have three attribution gaps. A missing jump table is reported at the beginning of its subprogram rather than at the gotox that needs the table, recursive-call details are present only in the legacy log, and the early subprogram-layout checks run before BTF line information is installed. Pass the failing gotox instruction into the jump-table lookup and include both ends of a recursive edge in the structured reason. The BTF validator needs the discovered subprogram boundaries together with the LD_ABS and tail-call properties collected during the layout scan. Split that property collection from layout validation, then validate BTF before reporting layout errors. This makes validated source information available to the jump-boundary and fallthrough reports without changing either check. Link: https://lore.kernel.org/bpf/cf2f420c2b21de440a7dc51b1565c0f06d4b539640ee5c03384e5d77bcfb5686@mail.kernel.org/ Signed-off-by: Kumar Kartikeya Dwivedi <[email protected]> --- kernel/bpf/cfg.c | 6 +++--- kernel/bpf/verifier.c | 47 ++++++++++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c index 0f13c13f4133..95c59f6cf70a 100644 --- a/kernel/bpf/cfg.c +++ b/kernel/bpf/cfg.c @@ -287,7 +287,7 @@ static struct bpf_iarray *jt_from_map(struct bpf_map *map) * combined jump table in jt->items (allocated with kvcalloc) */ static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env, - int subprog_start, int subprog_end) + int insn_idx, int subprog_start, int subprog_end) { struct bpf_iarray *jt = NULL; struct bpf_map *map; @@ -327,7 +327,7 @@ static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env, if (!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, insn_idx, "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); @@ -349,7 +349,7 @@ create_jt(int t, struct bpf_verifier_env *env) subprog = bpf_find_containing_subprog(env, t); subprog_start = subprog->start; subprog_end = (subprog + 1)->start; - jt = jt_from_subprog(env, subprog_start, subprog_end); + jt = jt_from_subprog(env, t, subprog_start, subprog_end); if (IS_ERR(jt)) return jt; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3de9e4f617b6..d2f08c6612c6 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2995,15 +2995,13 @@ static int add_kfuncs(struct bpf_verifier_env *env) return 0; } -static int check_subprogs(struct bpf_verifier_env *env) +static void find_subprog_properties(struct bpf_verifier_env *env) { - int i, subprog_start, subprog_end, off, cur_subprog = 0; + int i, subprog_end, cur_subprog = 0; struct bpf_subprog_info *subprog = env->subprog_info; struct bpf_insn *insn = env->prog->insnsi; int insn_cnt = env->prog->len; - /* now check that all jumps are within the same subprog */ - subprog_start = subprog[cur_subprog].start; subprog_end = subprog[cur_subprog + 1].start; for (i = 0; i < insn_cnt; i++) { u8 code = insn[i].code; @@ -3017,6 +3015,27 @@ static int check_subprogs(struct bpf_verifier_env *env) if (BPF_CLASS(code) == BPF_LD && (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND)) subprog[cur_subprog].has_ld_abs = true; + if (i == subprog_end - 1) { + cur_subprog++; + if (cur_subprog < env->subprog_cnt) + subprog_end = subprog[cur_subprog + 1].start; + } + } +} + +static int check_subprogs(struct bpf_verifier_env *env) +{ + int i, subprog_start, subprog_end, off, cur_subprog = 0; + struct bpf_subprog_info *subprog = env->subprog_info; + struct bpf_insn *insn = env->prog->insnsi; + int insn_cnt = env->prog->len; + + /* now check that all jumps are within the same subprog */ + subprog_start = subprog[cur_subprog].start; + subprog_end = subprog[cur_subprog + 1].start; + for (i = 0; i < insn_cnt; i++) { + u8 code = insn[i].code; + if (BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) goto next; if (BPF_OP(code) == BPF_CALL) @@ -3038,9 +3057,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) && @@ -3126,8 +3146,9 @@ static int sort_subprogs_topo(struct bpf_verifier_env *env) bpf_diag_program_structure( env, idx, "recursive subprogram call", "Rewrite the recursion as an explicit bounded loop, or split the logic so subprogram calls do not form a cycle.", - "This bpf2bpf call would make the subprogram call graph recursive. " - "The verifier requires a finite, acyclic call graph so it can bound stack depth and analysis."); + "The call from %s() to %s() would make the subprogram call graph recursive. " + "The verifier requires a finite, acyclic call graph so it can bound stack depth and analysis.", + bpf_subprog_name(env, cur), bpf_subprog_name(env, callee)); ret = -EINVAL; goto out; } @@ -21124,17 +21145,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 and apply CO-RE 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 and apply CO-RE. */ - ret = bpf_check_btf_info(env, attr, uattr); + ret = check_subprogs(env); if (ret < 0) goto skip_full_check; -- 2.53.0