From: sashiko-bot@kernel.org
To: "Jay Wang" <wanjay@amazon.com>,
bpf@vger.kernel.org, "Alexei Starovoitov" <ast@kernel.org>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Andrii Nakryiko" <andrii@kernel.org>,
"Eduard Zingerman" <eddyz87@gmail.com>,
"Kumar Kartikeya Dwivedi" <memxor@gmail.com>
Cc: "Ihor Solodrai" <ihor.solodrai@linux.dev>,
"Alan Maguire" <almagui@kernel.org>,
"Martin KaFai Lau" <martin.lau@linux.dev>,
"Yonghong Song" <yonghong.song@linux.dev>,
"Jiri Olsa" <jolsa@kernel.org>,
"Steven Rostedt" <rostedt@goodmis.org>,
"Masami Hiramatsu" <mhiramat@kernel.org>,
"Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>,
linux-trace-kernel@vger.kernel.org,
"Nathan Chancellor" <nathan@kernel.org>,
"Nicolas Schier" <nsc@kernel.org>,
linux-kbuild@vger.kernel.org, "Arnd Bergmann" <arnd@arndb.de>,
x86@kernel.org, "Hazem Mohamed Abuelfotoh" <abuehaze@amazon.com>,
"Bjoern Doebel" <doebel@amazon.de>,
"Martin Pohlack" <mpohlack@amazon.de>,
jay.wang.upstream@gmail.com
Subject: Re: [PATCH bpf-next 02/13] bpf: fetch the vmlinux BTF where kernel types enter a program
Date: Sat, 31 Oct 2026 13:42:47 +0000 [thread overview]
Message-ID: <sashiko-outbox-191985@kernel.org> (raw)
In-Reply-To: <20261031130959.23472-3-wanjay@amazon.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] bpf: NULL or ERR_PTR btf_vmlinux dereference via check_pseudo_btf_id()
- [Low] bpf: unreachable !btf check in check_ptr_to_map_access() when BTF is disabled
- [Low] bpf: unresolved TODO-NEEDS-IHOR Signed-off-by placeholder in commit log
--
commit a99de2b5b9828d221e9bcde23ac921cf3c9fc798
Author: Jay Wang <wanjay@amazon.com>
bpf: fetch the vmlinux BTF where kernel types enter a program
This commit drops the up-front vmlinux BTF fetch in bpf_check() and instead
fetches the vmlinux BTF on demand at the points where kernel types enter the
verifier state, avoiding unnecessary BTF parsing for programs that do not use
kernel types.
> bpf: fetch the vmlinux BTF where kernel types enter a program
>
> bpf_check() fetches the vmlinux BTF up front for every program, whether
> the program uses kernel types or not. That is free today because the
> BTF is parsed at boot anyway. Once it can be parsed on first use
> (CONFIG_DEBUG_INFO_BTF_LAZY, later in this series), it would make every
> program load pay for parsing 5-7 MB of BTF, including the socket filters
> systemd loads at boot.
>
> Drop the up-front fetch and fetch where kernel types enter the verifier
> state instead:
>
> - bpf_add_kfunc_call(), for the first kfunc call of a program;
> - check_ptr_to_map_access(), for map_ptr field access;
> - check_pseudo_btf_id(), for ldimm64 of a kernel variable (ksym);
> - check_helper_call(), for helpers whose prototype takes
> ARG_PTR_TO_BTF_ID or returns RET_PTR_TO_BTF_ID, and for
> bpf_snprintf_btf() and bpf_seq_printf_btf(), which take a kernel
> type id inside struct btf_ptr (helper_uses_vmlinux_btf()).
>
> The other ways in already fetch the BTF themselves: attach_btf in
> bpf_prog_load(), struct_ops map creation, CO-RE candidate lookup,
> bpf_find_btf_id(), btf_prepare_func_args() for kernel-typed arguments
> and the kernel context type lookups. The remaining reads of the
> btf_vmlinux global in the verifier are on paths dominated by one of
> these fetches. A program that uses none of them, such as a socket
> filter, does not need the vmlinux BTF any more, and verifies the same
> whether or not it has been parsed.
>
> Four related changes keep it that way:
>
> - The "in-kernel BTF is malformed" check in bpf_check() read the
> global right after the up-front fetch. Without the fetch it would
> pass or fail depending on whether something else had parsed the BTF
> before, so drop it; every fetch point reports a missing BTF itself
> and passes on an error of the parse, as that check did.
>
> - check_ptr_to_map_access() checks allow_ptr_leaks before it fetches
> the BTF, and bpf_prog_load() falls back to the vmlinux BTF for an
> attach_btf_id only with CAP_BPF, which all program types that take
> one need, so that an unprivileged load cannot make the kernel parse
> it only to be refused. The map_ptr permission error can then not
> name the map's struct any more and says "map_ptr access is allowed
> only to CAP_PERFMON and CAP_SYS_ADMIN". A ksym, which unprivileged
> programs may use, still parses it.
>
> - Add bpf_peek_btf_vmlinux(), which returns the vmlinux BTF only if it
> is parsed already, and use it in bpf_btf_printf_prepare(). The BTF
> printf helpers run in program context, which may be atomic, and the
> verifier has fetched the BTF for every program that calls them.
>
> - is_kfunc_dynptr_write(), reached from the argument checks that
> helpers and kfuncs share, compared meta->btf with the global, which
> a helper's NULL matches while the global is NULL. Check that the
> call is a kfunc first, as the other kfunc predicates there do.
>
> Helpers taking ARG_PTR_TO_BTF_ID_SOCK_COMMON are left out on purpose:
> they take sockets without BTF types too, the shared argument check reads
> the global only for a PTR_TO_BTF_ID register, and every source of such
> a register fetched the BTF.
>
> No functional change with CONFIG_DEBUG_INFO_BTF=y, where kfunc
> registrations at boot parse the BTF, other than that error message and
> that, if the vmlinux BTF fails to parse (a broken toolchain), programs
> that use no kernel types now load instead of failing. Without BTF the
> new helper check is compiled out and the existing error paths are
> unchanged.
>
> Co-developed-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> TODO-NEEDS-IHOR: Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> Signed-off-by: Jay Wang <wanjay@amazon.com>
[Severity: Low]
This isn't a bug in the code, but the commit log still has an unresolved
TODO-NEEDS-IHOR prefix on the Signed-off-by trailer following
Co-developed-by. Should that placeholder be resolved before merging?
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 5601d20562186..1b901e3cb3c22 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -6867,30 +6883,33 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
> enum bpf_type_flag flag = 0;
> const struct btf_type *t;
> const char *tname;
> + struct btf *btf;
> u32 btf_id;
> int ret;
>
> - if (!btf_vmlinux) {
> - verbose(env, "map_ptr access not supported without CONFIG_DEBUG_INFO_BTF\n");
> - return -ENOTSUPP;
> - }
> -
> if (!map->ops->map_btf_id || !*map->ops->map_btf_id) {
> verbose(env, "map_ptr access not supported for map type %d\n",
> map->map_type);
> return -ENOTSUPP;
> }
>
> - t = btf_type_by_id(btf_vmlinux, *map->ops->map_btf_id);
> - tname = btf_name_by_offset(btf_vmlinux, t->name_off);
> -
> + /* before the BTF fetch: unprivileged loads must not parse it */
> if (!env->allow_ptr_leaks) {
> - verbose(env,
> - "'struct %s' access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n",
> - tname);
> + verbose(env, "map_ptr access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN\n");
> return -EPERM;
> }
>
> + btf = bpf_get_btf_vmlinux();
> + if (IS_ERR(btf))
> + return vmlinux_btf_err(env, btf);
> + if (!btf) {
> + verbose(env, "map_ptr access not supported without CONFIG_DEBUG_INFO_BTF\n");
> + return -ENOTSUPP;
> + }
[Severity: Low]
Can this !btf check in check_ptr_to_map_access() ever be reached?
When CONFIG_DEBUG_INFO_BTF is disabled, BTF_ID_LIST_SINGLE() and
BTF_ID_LIST_GLOBAL_SINGLE() in include/linux/btf_ids.h define zero-initialized
arrays:
#define BTF_ID_LIST_SINGLE(name, prefix, typename) static u32 __maybe_unused name[1];
#define BTF_ID_LIST_GLOBAL_SINGLE(name, prefix, typename) u32 __maybe_unused name[1];
Because *map->ops->map_btf_id is 0 for all map types when
CONFIG_DEBUG_INFO_BTF is disabled, moving bpf_get_btf_vmlinux() after the
!*map->ops->map_btf_id check causes check_ptr_to_map_access() to always exit
early with "map_ptr access not supported for map type %d", leaving the !btf
branch unreachable.
[ ... ]
> @@ -20898,12 +20941,14 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
> return -EINVAL;
> }
> } else {
> - if (!btf_vmlinux) {
> + btf = bpf_get_btf_vmlinux();
> + if (IS_ERR(btf))
> + return vmlinux_btf_err(env, btf);
> + if (!btf) {
> verbose(env, "kernel is missing BTF, make sure CONFIG_DEBUG_INFO_BTF=y is specified in Kconfig.\n");
> return -EINVAL;
> }
> - btf_get(btf_vmlinux);
> - btf = btf_vmlinux;
> + btf_get(btf);
> }
>
> err = __check_pseudo_btf_id(env, insn, aux, btf);
[Severity: High]
Can a program create a PTR_TO_BTF_ID register through check_pseudo_btf_id()
without btf_vmlinux being fetched or validated, leading to a NULL or ERR_PTR
dereference of btf_vmlinux in check_func_arg()?
In check_helper_call(), bpf_get_btf_vmlinux() is only called when
IS_ENABLED(CONFIG_DEBUG_INFO_BTF) && helper_uses_vmlinux_btf(func_id, fn), and
helper_uses_vmlinux_btf() omits ARG_PTR_TO_BTF_ID_SOCK_COMMON under the
assumption that every source of a PTR_TO_BTF_ID register has already fetched
btf_vmlinux.
However, in check_pseudo_btf_id(), when btf_fd (insn[1].imm) is non-zero:
btf_fd = insn[1].imm;
if (btf_fd) {
btf = btf_get_by_fd(btf_fd);
if (IS_ERR(btf)) {
verbose(env, "invalid module BTF object FD specified.\n");
return -EINVAL;
}
} else {
btf = bpf_get_btf_vmlinux();
...
}
btf_get_by_fd() accepts any BTF fd, including a user-loaded BTF created via
BPF_BTF_LOAD (which does not fetch btf_vmlinux), and check_pseudo_btf_id()
does not verify btf_is_module(btf) or btf_is_kernel(btf). If a user loads a
BTF containing a BTF_KIND_STRUCT and a BTF_KIND_VAR whose name matches a
kernel symbol in kallsyms, __check_pseudo_btf_id() sets
aux->btf_var.reg_type = PTR_TO_BTF_ID and check_ld_imm() marks the destination
register as PTR_TO_BTF_ID while btf_vmlinux is still NULL (with
CONFIG_DEBUG_INFO_BTF_LAZY=y before first parse, or CONFIG_DEBUG_INFO_BTF=n)
or ERR_PTR (if btf_parse_vmlinux() failed).
When that PTR_TO_BTF_ID register is then passed to a helper taking
ARG_PTR_TO_BTF_ID_SOCK_COMMON (or ARG_PTR_TO_BTF_ID when
CONFIG_DEBUG_INFO_BTF=n), check_func_arg() does:
case ARG_PTR_TO_BTF_ID:
case ARG_PTR_TO_BTF_ID_SOCK_COMMON:
{
const u32 *arg_btf_id = fn->arg_btf_id[arg];
const struct btf *arg_btf = is_kfunc(meta) ? meta->btf : btf_vmlinux;
...
if (!meta->btf) {
...
if (base_type(reg->type) != PTR_TO_BTF_ID)
break;
}
...
if (!process_arg_ptr_to_btf_id(env, reg, regno, arg_btf_id, arg_btf, meta))
which passes arg_btf = btf_vmlinux (NULL or ERR_PTR) into
bpf_diag_fmt_btf_type() and process_arg_ptr_to_btf_id() ->
btf_type_skip_modifiers() -> btf_type_by_id(), dereferencing arg_btf.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261031130959.23472-1-wanjay@amazon.com?part=2
next prev parent reply other threads:[~2026-10-31 13:42 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 13:09 [PATCH bpf-next 00/13] bpf: keep the vmlinux BTF compressed until first use (CONFIG_DEBUG_INFO_BTF_LAZY) to save ~6 MB memory Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 01/13] bpf: match program context types by name without vmlinux BTF Jay Wang
2026-10-31 13:43 ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 02/13] bpf: fetch the vmlinux BTF where kernel types enter a program Jay Wang
2026-10-31 13:42 ` sashiko-bot [this message]
2026-10-31 13:09 ` [PATCH bpf-next 03/13] bpf: check CAP_SYS_ADMIN before looking up bpffs delegation names Jay Wang
2026-10-31 13:43 ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 04/13] bpf: move bpf_get_btf_vmlinux() to btf.c Jay Wang
2026-10-31 13:43 ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 05/13] bpf: let btf_parse_module() adopt the caller's copy of the module BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 06/13] bpf: look up the struct_ops common value type in the given BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 07/13] bpf: parse the vmlinux BTF before taking cand_cache_mutex Jay Wang
2026-10-31 13:43 ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 08/13] tracing: parse the vmlinux BTF when BTF-typed output is enabled Jay Wang
2026-10-31 13:37 ` sashiko-bot
2026-10-31 13:43 ` bot+bpf-ci
2026-10-31 13:09 ` [PATCH bpf-next 09/13] bpf: keep the vmlinux BTF zstd-compressed in the kernel image Jay Wang
2026-10-31 13:33 ` sashiko-bot
2026-10-31 13:09 ` [PATCH bpf-next 10/13] bpf: defer kfunc and struct_ops registrations until first BTF use Jay Wang
2026-10-31 13:38 ` sashiko-bot
2026-10-31 13:09 ` [PATCH bpf-next 11/13] bpf: keep module BTF until the vmlinux BTF is parsed Jay Wang
2026-10-31 13:37 ` sashiko-bot
2026-10-31 13:09 ` [PATCH bpf-next 12/13] selftests/bpf: keep ksyms and linked_vars working without __start_BTF Jay Wang
2026-10-31 13:09 ` [PATCH bpf-next 13/13] Documentation/bpf: describe CONFIG_DEBUG_INFO_BTF_LAZY Jay Wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-191985@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=abuehaze@amazon.com \
--cc=almagui@kernel.org \
--cc=andrii@kernel.org \
--cc=arnd@arndb.de \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=doebel@amazon.de \
--cc=eddyz87@gmail.com \
--cc=ihor.solodrai@linux.dev \
--cc=jay.wang.upstream@gmail.com \
--cc=jolsa@kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=mathieu.desnoyers@efficios.com \
--cc=memxor@gmail.com \
--cc=mhiramat@kernel.org \
--cc=mpohlack@amazon.de \
--cc=nathan@kernel.org \
--cc=nsc@kernel.org \
--cc=rostedt@goodmis.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=wanjay@amazon.com \
--cc=x86@kernel.org \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.