Files
Amery Hung 2bd4a975ea bpf: Admit kfunc argument registers through check_reg_type()
check_kfunc_args() open-codes exact register-type tests in most of
its per-argument cases, duplicating what compatible_reg_types[]
already expresses for helpers. This leaves two admission paths and
prevents the helper and kfunc loops from converging.

Runtime argument resolution now converts a scalar-struct BTF
argument to fixed-size memory before register admission. Give the
remaining kfunc-only argument kinds compatibility entries and run
check_reg_type() once before the per-kind switch.

Kfunc memory arguments already accept BPF-allocated objects.
Normalize only the local comparison type to PTR_TO_MEM; subsequent
memory checks still inspect the original register type. Keep
allocated-object forms out of mem_types so helper calls continue
through the existing type-mismatch path and retain its diagnostic.

For ARG_PTR_TO_BTF_ID, let check_reg_type() admit BTF-backed
register types and reject incompatible register classes with its
standard diagnostic. Remove the now-unused lookup_reg2btf_ids().
Exact BTF identity and trust requirements remain checked later by
process_arg_ptr_to_btf_id(). ARG_IGNORE and ARG_PTR_TO_PROG_AUX remain
skipped because the verifier does not read those arguments from the
program.

Iterator arguments use the stack-pointer table. Graph nodes and
ARG_PTR_TO_REFCOUNTED_KPTR share an allocated-object table. It
admits owning and borrowed objects, including RCU-protected forms.
Their switch cases retain API-specific ownership and BTF-record
validation.
ARG_PTR_TO_ALLOC_BTF_ID uses a separate table for object-drop arguments.

Rename timer_types to map_value_types now that ARG_PTR_TO_WORKQUEUE
and ARG_PTR_TO_TASK_WORK share it. Similarly, rename spin_lock_types
to map_value_or_alloc_obj_types because graph roots and resource spin
locks share its map-value-or-allocated-object admission.

Moving admission checks into check_reg_type() must not discard the
structured call-argument diagnostics emitted by the individual cases.
Add bpf_diag_arg_type_plain() alongside bpf_diag_reg_type_plain() and
use it to preserve the existing per-kind Pass suggestions where
available. Other argument kinds retain the generic suggestion. The
reason continues to report the actual register type and all accepted
register types.

Two behavior changes fall out of running admission first:

  - ARG_CONST_MEM_SIZE reaches process_const_arg(), and through it
    mark_chain_precision(), only after the register is known to be a
    scalar. Passing a pointer as a __szk argument used to reach
    backtrack_insn() with a non-scalar and trip the backtracking-misuse
    verifier bug.

  - ARG_CONST_MAP_PTR no longer needs its own type_may_be_null()
    test, because check_reg_type() compares whole register types.

Every kfunc argument that is not explicitly ignored now passes
through check_reg_type(), followed by the common register-offset check
in the same order as a helper argument.

Signed-off-by: Amery Hung <ameryhung@gmail.com>
Link: https://lore.kernel.org/r/20260911220415.1396439-19-ameryhung@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2026-09-11 20:16:04 -07:00

113 lines
5.5 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
#ifndef __BPF_DIAGNOSTICS_H
#define __BPF_DIAGNOSTICS_H
#include <linux/bpf.h>
#include <linux/compiler_attributes.h>
#include <linux/stdarg.h>
#include <linux/types.h>
struct bpf_reference_state;
struct bpf_func_state;
struct bpf_reg_state;
struct bpf_verifier_env;
struct bpf_verifier_state;
struct btf;
const char *bpf_diag_fmt_s64_sum(struct bpf_verifier_env *env, s64 value, int addend);
enum bpf_diag_mod_reason {
BPF_DIAG_MOD_WRITE,
BPF_DIAG_MOD_SPILL,
BPF_DIAG_MOD_VAR_WRITE,
BPF_DIAG_MOD_REF_RELEASE,
BPF_DIAG_MOD_PKT_DATA_CHANGE,
BPF_DIAG_MOD_NON_OWN_REF,
BPF_DIAG_MOD_CALLER_SAVED,
};
enum bpf_diag_context_kind {
BPF_DIAG_CONTEXT_NONE,
BPF_DIAG_CONTEXT_RCU,
BPF_DIAG_CONTEXT_PREEMPT,
BPF_DIAG_CONTEXT_IRQ,
BPF_DIAG_CONTEXT_LOCK,
};
enum bpf_diag_invalid_deref_kind {
BPF_DIAG_DEREF_SCALAR,
BPF_DIAG_DEREF_NULLABLE_PTR,
BPF_DIAG_DEREF_MODIFIED_PTR,
BPF_DIAG_DEREF_INVALID_PTR,
};
bool bpf_diag_enabled(const struct bpf_verifier_env *env);
int bpf_diag_init(struct bpf_verifier_env *env);
void bpf_diag_init_frame(struct bpf_verifier_env *env, struct bpf_func_state *state);
char *bpf_diag_fmt_buf(struct bpf_verifier_env *env, size_t size);
const char *bpf_diag_vfmt(struct bpf_verifier_env *env, const char *fmt, va_list args)
__printf(2, 0);
const char *bpf_diag_fmt(struct bpf_verifier_env *env, const char *fmt, ...) __printf(2, 3);
const char *bpf_diag_fmt_btf_type(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id);
const char *bpf_diag_reg_type_plain(struct bpf_verifier_env *env, enum bpf_reg_type type);
const char *bpf_diag_arg_type_plain(enum bpf_arg_type type);
u64 bpf_diag_event_log_save(struct bpf_verifier_env *env);
void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos);
u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state);
void bpf_diag_free(struct bpf_verifier_env *env);
void bpf_diag_register_type(struct bpf_verifier_env *env, u32 insn_idx, int regno,
const char *problem, const char *reason, const char *suggestion);
void bpf_diag_invalid_deref(struct bpf_verifier_env *env, u32 insn_idx, int regno,
const char *reg_name, const struct bpf_reg_state *reg,
enum bpf_diag_invalid_deref_kind kind, s64 offset);
void bpf_diag_unreadable_reg(struct bpf_verifier_env *env, u32 insn_idx, int regno);
void bpf_diag_stack_arg_uninit(struct bpf_verifier_env *env, u32 insn_idx, int nargs,
int stack_arg_slot, const char *callee_name,
const char *arg_name);
void bpf_diag_memory(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
const char *reason, const char *suggestion);
void bpf_diag_mem_bounds(struct bpf_verifier_env *env, u32 insn_idx, int regno,
const char *reg_name, const char *type_name, const char *proof,
int off, int size, u32 mem_size, const struct bpf_reg_state *reg);
void bpf_diag_res(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
const char *reason, const char *suggestion);
void bpf_diag_lock(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
const char *reason, const char *suggestion,
const struct bpf_reference_state *active_lock);
void bpf_diag_irq(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
const char *reason, const char *suggestion, u32 depth);
void bpf_diag_leak(struct bpf_verifier_env *env, u32 ref_id, u32 alloc_insn, u32 fail_insn);
void bpf_diag_call_type(struct bpf_verifier_env *env, u32 insn_idx, int argno, int regno,
int stack_arg_slot, const char *call_name, const char *arg_name,
const char *reason, const char *suggestion);
void bpf_diag_ctx_forbidden(struct bpf_verifier_env *env, u32 insn_idx,
const char *operation, const char *suggestion);
void bpf_diag_ctx_active(struct bpf_verifier_env *env, u32 insn_idx, const char *operation,
enum bpf_diag_context_kind ctx_kind, const char *suggestion);
void bpf_diag_ctx_required(struct bpf_verifier_env *env, u32 insn_idx, const char *operation,
enum bpf_diag_context_kind ctx_kind, const char *suggestion);
void bpf_diag_ctx_underflow(struct bpf_verifier_env *env, u32 insn_idx,
const char *operation, enum bpf_diag_context_kind ctx_kind,
const char *suggestion);
void bpf_diag_program_structure(struct bpf_verifier_env *env, u32 insn_idx,
const char *problem, const char *suggestion,
const char *reason_fmt, ...) __printf(5, 6);
void bpf_diag_policy(struct bpf_verifier_env *env, u32 insn_idx, const char *operation,
const char *reason, const char *suggestion);
void bpf_diag_record_branch(struct bpf_verifier_env *env, u32 insn_idx, bool cond_true);
void bpf_diag_mod_begin(struct bpf_verifier_env *env, const struct bpf_reg_state *reg,
const struct bpf_reg_state *origin, enum bpf_diag_mod_reason reason);
void bpf_diag_mod_end(struct bpf_verifier_env *env);
void bpf_diag_record_scrub(struct bpf_verifier_env *env, const struct bpf_reg_state *reg,
enum bpf_diag_mod_reason reason);
void bpf_diag_record_scrub_stack(struct bpf_verifier_env *env,
const struct bpf_func_state *state, s16 min_off, s16 max_off,
enum bpf_diag_mod_reason reason);
void bpf_diag_record_ref_acquire(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);
void bpf_diag_record_ref_release(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);
void bpf_diag_record_context(struct bpf_verifier_env *env, u32 insn_idx,
enum bpf_diag_context_kind ctx_kind, bool enter, u32 depth);
#endif /* __BPF_DIAGNOSTICS_H */