The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Eduard Zingerman <eddyz87@gmail.com>
To: Ihor Solodrai <ihor.solodrai@linux.dev>,
	Alexei Starovoitov <ast@kernel.org>,
	 Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Kumar Kartikeya Dwivedi	 <memxor@gmail.com>
Cc: Tejun Heo <tj@kernel.org>,
	bpf@vger.kernel.org,  linux-kernel@vger.kernel.org,
	kernel-team@meta.com
Subject: Re: [PATCH bpf v2 1/2] bpf: Fix tracing of kfuncs with implicit args
Date: Sat, 11 Jul 2026 03:43:14 -0700	[thread overview]
Message-ID: <a183b4d6f3e814f9ffb79c8e0efca8b5f162e127.camel@gmail.com> (raw)
In-Reply-To: <20260710192940.3020280-2-ihor.solodrai@linux.dev>

On Fri, 2026-07-10 at 12:29 -0700, Ihor Solodrai wrote:

[...]

> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 64572f85edc8..20aeca6b4f95 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -9114,6 +9114,26 @@ u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_p
>  	return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
>  }
>  
> +/*
> + * Return the union of a kfunc's flags across all hooks.
> + * Unlike btf_kfunc_flags(), not restricted to a calling
> + * program's hook. Used when attaching to a kfunc for tracing.
> + */
> +u32 btf_kfunc_accumulated_flags(const struct btf *btf, u32 kfunc_btf_id)
> +{
> +	enum btf_kfunc_hook hook;
> +	u32 *hook_flags;
> +	u32 flags = 0;
> +
> +	for (hook = 0; hook < BTF_KFUNC_HOOK_MAX; hook++) {
> +		hook_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
> +		if (hook_flags)
> +			flags |= *hook_flags;
> +	}
> +
> +	return flags;
> +}

This. Is. So. Ugly. You guys should reflect a bit more on the llm output.

The fact that we currently have a technical possibility to have the same
kfunc in different sets with different flags seem to be accidental.
Is there ever a valid scenario for this?
Grepping kernel code all I can find are these kfuncs from
drivers/hid/bpf/hid_bpf_dispatch.c:

  BTF_KFUNCS_START(hid_bpf_kfunc_ids)
  ...
  BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
  BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE)
  BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE)
  BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE)
  BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE)
  BTF_ID_FLAGS(func, hid_bpf_try_input_report)
  BTF_KFUNCS_END(hid_bpf_kfunc_ids)
  ...
  /* for syscall HID-BPF */
  BTF_KFUNCS_START(hid_bpf_syscall_kfunc_ids)
  BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL)
  BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE)
  BTF_ID_FLAGS(func, hid_bpf_hw_request)
  BTF_ID_FLAGS(func, hid_bpf_hw_output_report)
  BTF_ID_FLAGS(func, hid_bpf_input_report)
  BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids)
  ...
  register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set);

But this appears to be a misuse:
(a) syscall programs are always sleepable
(b) e.g. hid_bpf_allocate_context() calls kzalloc_obj() which can sleep,
    as far as I understand. Meaning that hid_bpf_allocate_context()
    should always be marked with KF_SLEEPABLE.

Am I confused?

If I am not confused, I think that resolve_btfids() has to verify that
kfunc flags are always the same across multiple sets.

> +
>  u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
>  				const struct bpf_prog *prog)
>  {
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 6515d4d3c003..2f56ab8d6b58 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -2584,24 +2584,24 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
>  
>  #define KF_IMPL_SUFFIX "_impl"
>  
> -static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
> -						    struct btf *btf,
> -						    const char *func_name)
> +static const struct btf_type *
> +find_kfunc_impl_proto(struct bpf_verifier_log *log, struct btf *btf, const char *func_name)

Nit: no need to reformat the declaration.

>  {
> -	char *buf = env->tmp_str_buf;
>  	const struct btf_type *func;
> +	char buf[KSYM_NAME_LEN];

We attempt to avoid large allocations on stack, that's why tmp_str_buf
was used here in a first place. This is the second time I see llm doing this,
maybe adjust your agents.md or something. If tmp_str_buf is not big enough,
that can be adjusted. But I don't think it's necessary in this particular case.

>  	s32 impl_id;
>  	int len;
>  
> -	len = snprintf(buf, TMP_STR_BUF_LEN, "%s%s", func_name, KF_IMPL_SUFFIX);
> -	if (len < 0 || len >= TMP_STR_BUF_LEN) {
> -		verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
> +	len = snprintf(buf, sizeof(buf), "%s%s", func_name, KF_IMPL_SUFFIX);
> +	if (len < 0 || len >= sizeof(buf)) {
> +		bpf_log(log, "function name %s%s is too long\n",
> +			func_name, KF_IMPL_SUFFIX);
>  		return NULL;
>  	}
>  
>  	impl_id = btf_find_by_name_kind(btf, buf, BTF_KIND_FUNC);
>  	if (impl_id <= 0) {
> -		verbose(env, "cannot find function %s in BTF\n", buf);
> +		bpf_log(log, "cannot find function %s in BTF\n", buf);
>  		return NULL;
>  	}
>  
> @@ -2653,7 +2653,7 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
>  	 * can be found through the counterpart _impl kfunc.
>  	 */
>  	if (kfunc_flags && (*kfunc_flags & KF_IMPLICIT_ARGS))
> -		func_proto = find_kfunc_impl_proto(env, btf, func_name);
> +		func_proto = find_kfunc_impl_proto(&env->log, btf, func_name);
>  	else
>  		func_proto = btf_type_by_id(btf, func->type);
>  
> @@ -18873,6 +18873,44 @@ static int btf_id_allow_sleepable(u32 btf_id, unsigned long addr, const struct b
>  	return -EINVAL;
>  }
>  
> +/*
> + * Resolve the prototype describing a trace target's real ABI. A
> + * KF_IMPLICIT_ARGS kfunc has its injected args stripped from the public
> + * prototype, so use the _impl prototype; other targets use their own.
> + */
> +static const struct btf_type *
> +btf_attach_func_proto(struct bpf_verifier_log *log, struct btf *btf, u32 func_id)
> +{
> +	const struct btf_type *func;
> +	struct module *mod = NULL;
> +	const char *name;
> +	u32 kfunc_flags;
> +
> +	func = btf_type_by_id(btf, func_id);
> +	if (!func || !btf_type_is_func(func))
> +		return NULL;
> +
> +	/*
> +	 * btf_kfunc_accumulated_flags() reads kfunc_set_tab, which for a
> +	 * module is stable only once it is live; hold a module ref across
> +	 * the read to exclude a concurrent module load.
> +	 */
> +	if (btf_is_module(btf)) {

So, this function is supplied with a module BTF,
and the argument is that we can have an alive BTF reference
but not to hold a live module reference?

References to kfunc's modules are stored in the
bpf_prog_aux->kfunc_btf_tab, and the following code from core.c
suggests that module references would be present for as long
as the program using BTF from the module is present:

  static void bpf_prog_free_deferred(struct work_struct *work)
  {
	...
	bpf_free_kfunc_btf_tab(aux->kfunc_btf_tab);
	...
  }

?

> +		mod = btf_try_get_module(btf);
> +		if (!mod)
> +			return NULL;
> +	}
> +	kfunc_flags = btf_kfunc_accumulated_flags(btf, func_id);
> +	module_put(mod);
> +
> +	if (kfunc_flags & KF_IMPLICIT_ARGS) {
> +		name = btf_name_by_offset(btf, func->name_off);
> +		return find_kfunc_impl_proto(log, btf, name);
> +	}
> +
> +	return btf_type_by_id(btf, func->type);
> +}
> +
>  int bpf_check_attach_target(struct bpf_verifier_log *log,
>  			    const struct bpf_prog *prog,
>  			    const struct bpf_prog *tgt_prog,
> @@ -19121,8 +19159,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
>  		if (prog_extension &&
>  		    btf_check_type_match(log, prog, btf, t))
>  			return -EINVAL;
> -		t = btf_type_by_id(btf, t->type);
> -		if (!btf_type_is_func_proto(t))
> +		t = btf_attach_func_proto(log, btf, btf_id);
> +		if (!t || !btf_type_is_func_proto(t))

This part seems fine.

>  			return -EINVAL;
>  
>  		if ((prog->aux->saved_dst_prog_type || prog->aux->saved_dst_attach_type) &&
> @@ -19407,8 +19445,8 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt
>  		return -EINVAL;
>  	if (!btf_type_is_func(t))
>  		return -EINVAL;
> -	t = btf_type_by_id(btf, t->type);
> -	if (!btf_type_is_func_proto(t))
> +	t = btf_attach_func_proto(NULL, btf, btf_id);
> +	if (!t || !btf_type_is_func_proto(t))
>  		return -EINVAL;
>  	err = btf_distill_func_proto(NULL, btf, t, tname, &tgt_info->fmodel);
>  	if (err < 0)
> -- 
> 2.55.0

  reply	other threads:[~2026-07-11 10:43 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-10 19:29 [PATCH bpf v2 0/2] bpf: Fix tracing of kfuncs with implicit args Ihor Solodrai
2026-07-10 19:29 ` [PATCH bpf v2 1/2] " Ihor Solodrai
2026-07-11 10:43   ` Eduard Zingerman [this message]
2026-07-10 19:29 ` [PATCH bpf v2 2/2] selftests/bpf: Cover tracing implicit kfunc args Ihor Solodrai

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=a183b4d6f3e814f9ffb79c8e0efca8b5f162e127.camel@gmail.com \
    --to=eddyz87@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=ihor.solodrai@linux.dev \
    --cc=kernel-team@meta.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=memxor@gmail.com \
    --cc=tj@kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox