[PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS

Ihor Solodrai posted 10 patches 4 weeks, 1 day ago
There is a newer version of this series
[PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 4 weeks, 1 day ago
A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
expected to have two associated types in BTF:
  * `bpf_foo` with a function prototype that omits implicit arguments
  * `bpf_foo_impl` with a function prototype that matches the kernel
     declaration of `bpf_foo`, but doesn't have a ksym associated with
     its name

In order to support kfuncs with implicit arguments, the verifier has
to know how to resolve a call of `bpf_foo` to the correct BTF function
prototype and address.

To implement this, in add_kfunc_call() kfunc flags are checked for
KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
convention) function in BTF.

This effectively changes the signature of the `bpf_foo` kfunc in the
context of verification: from one without implicit args to the one
with full argument list.

Whether a kfunc argument is implicit or not is determined by
is_kfunc_arg_implicit(). The values of implicit arguments by design
are provided by the verifier, and so they can only be of particular
types. In this patch the only allowed implicit arg type is a pointer
to struct bpf_prog_aux. The __prog args (usually void *) are also
considered implicit for backwards compatibility.

In order to enable the verifier to correctly set an implicit
bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
check for the arg type. At a point when prog arg is determined in
check_kfunc_args() the kfunc with implicit args already has a
prototype with full argument list, so the existing value patch
mechanism just works.

If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
that uses a __prog argument (a legacy case), the prototype
substitution works in exactly the same way, assuming the kfunc follows
the _impl naming convention. The difference is only in how _impl
prototype is added to the BTF, which is not the verifier's
concern. See a subsequent resolve_btfids patch for details.

In check_kfunc_call() reset the subreg_def of registers holding
implicit arguments to correctly track zero extensions.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 include/linux/btf.h   |  1 +
 kernel/bpf/verifier.c | 70 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index bd261015c4bc..f64cc40ab96f 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -78,6 +78,7 @@
 #define KF_ARENA_RET    (1 << 13) /* kfunc returns an arena pointer */
 #define KF_ARENA_ARG1   (1 << 14) /* kfunc takes an arena pointer as its first argument */
 #define KF_ARENA_ARG2   (1 << 15) /* kfunc takes an arena pointer as its second argument */
+#define KF_IMPLICIT_ARGS (1 << 16) /* kfunc has implicit arguments supplied by the verifier */
 
 /*
  * Tag marking a kernel function as a kfunc. This is meant to minimize the
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 9618e4c37fce..5541ab674e30 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
 	return btf_vmlinux ?: ERR_PTR(-ENOENT);
 }
 
+#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)
+{
+	char impl_name[KSYM_SYMBOL_LEN];
+	const struct btf_type *func;
+	s32 impl_id;
+	int len;
+
+	len = snprintf(impl_name, sizeof(impl_name), "%s%s", func_name, KF_IMPL_SUFFIX);
+	if (len < 0 || len >= sizeof(impl_name)) {
+		verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
+		return NULL;
+	}
+
+	impl_id = btf_find_by_name_kind(btf, impl_name, BTF_KIND_FUNC);
+	if (impl_id <= 0) {
+		verbose(env, "cannot find function %s in BTF\n", impl_name);
+		return NULL;
+	}
+
+	func = btf_type_by_id(btf, impl_id);
+	if (!func || !btf_type_is_func(func)) {
+		verbose(env, "%s (btf_id %d) is not a function\n", impl_name, impl_id);
+		return NULL;
+	}
+
+	return btf_type_by_id(btf, func->type);
+}
+
 static int fetch_kfunc_meta(struct bpf_verifier_env *env,
 			    s32 func_id,
 			    s16 offset,
@@ -3308,7 +3340,16 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
 	}
 
 	func_name = btf_name_by_offset(btf, func->name_off);
-	func_proto = btf_type_by_id(btf, func->type);
+
+	/*
+	 * An actual prototype of a kfunc with KF_IMPLICIT_ARGS flag
+	 * can be found through the counterpart _impl kfunc.
+	 */
+	if (unlikely(kfunc_flags && KF_IMPLICIT_ARGS & *kfunc_flags))
+		func_proto = find_kfunc_impl_proto(env, btf, func_name);
+	else
+		func_proto = btf_type_by_id(btf, func->type);
+
 	if (!func_proto || !btf_type_is_func_proto(func_proto)) {
 		verbose(env, "kernel function btf_id %d does not have a valid func_proto\n",
 			func_id);
@@ -12173,9 +12214,16 @@ static bool is_kfunc_arg_irq_flag(const struct btf *btf, const struct btf_param
 	return btf_param_match_suffix(btf, arg, "__irq_flag");
 }
 
+static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param *arg);
+
 static bool is_kfunc_arg_prog(const struct btf *btf, const struct btf_param *arg)
 {
-	return btf_param_match_suffix(btf, arg, "__prog");
+	return btf_param_match_suffix(btf, arg, "__prog") || is_kfunc_arg_prog_aux(btf, arg);
+}
+
+static bool is_kfunc_arg_implicit(const struct btf *btf, const struct btf_param *arg)
+{
+	return is_kfunc_arg_prog(btf, arg);
 }
 
 static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,
@@ -12206,6 +12254,7 @@ enum {
 	KF_ARG_WORKQUEUE_ID,
 	KF_ARG_RES_SPIN_LOCK_ID,
 	KF_ARG_TASK_WORK_ID,
+	KF_ARG_PROG_AUX_ID
 };
 
 BTF_ID_LIST(kf_arg_btf_ids)
@@ -12217,6 +12266,7 @@ BTF_ID(struct, bpf_rb_node)
 BTF_ID(struct, bpf_wq)
 BTF_ID(struct, bpf_res_spin_lock)
 BTF_ID(struct, bpf_task_work)
+BTF_ID(struct, bpf_prog_aux)
 
 static bool __is_kfunc_ptr_arg_type(const struct btf *btf,
 				    const struct btf_param *arg, int type)
@@ -12297,6 +12347,11 @@ static bool is_kfunc_arg_callback(struct bpf_verifier_env *env, const struct btf
 	return true;
 }
 
+static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param *arg)
+{
+	return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_PROG_AUX_ID);
+}
+
 /* Returns true if struct is composed of scalars, 4 levels of nesting allowed */
 static bool __btf_type_is_scalar_struct(struct bpf_verifier_env *env,
 					const struct btf *btf,
@@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 	for (i = 0; i < nargs; i++) {
 		u32 regno = i + 1;
 
+		/*
+		 * Implicit kfunc arguments are set after main verification pass.
+		 * For correct tracking of zero-extensions we have to reset subreg_def for such
+		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
+		 * from an earlier (irrelevant) point in the program, which may lead to an error
+		 * in opt_subreg_zext_lo32_rnd_hi32().
+		 */
+		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
+				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
+			regs[regno].subreg_def = DEF_NOT_SUBREG;
+
 		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
 		if (btf_type_is_ptr(t))
 			mark_btf_func_reg_size(env, regno, sizeof(void *));
-- 
2.52.0
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Eduard Zingerman 3 weeks, 4 days ago
On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:

[...]

> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
>  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
>  }
>  
> +#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)
> +{
> +	char impl_name[KSYM_SYMBOL_LEN];

Oh, as we discussed already, this should use env->tmp_str_buf.

> +	const struct btf_type *func;
> +	s32 impl_id;
> +	int len;
> +
> +	len = snprintf(impl_name, sizeof(impl_name), "%s%s", func_name, KF_IMPL_SUFFIX);
> +	if (len < 0 || len >= sizeof(impl_name)) {
> +		verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
> +		return NULL;
> +	}

[...]
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 3 weeks, 4 days ago
On 1/13/26 1:59 PM, Eduard Zingerman wrote:
> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> 
> [...]
> 
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
>>  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
>>  }
>>  
>> +#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)
>> +{
>> +	char impl_name[KSYM_SYMBOL_LEN];
> 
> Oh, as we discussed already, this should use env->tmp_str_buf.

The env->tmp_str_buf size is smaller:

	#define TMP_STR_BUF_LEN 320

*And* there is already a local char buffer of size KSYM_SYMBOL_LEN
already in use in verifier.c:

	int bpf_check_attach_target(...) {
		bool prog_extension = prog->type == BPF_PROG_TYPE_EXT;
		bool prog_tracing = prog->type == BPF_PROG_TYPE_TRACING;
		char trace_symbol[KSYM_SYMBOL_LEN];
	[...]

Since these are function names, the real limit is KSYM_SYMBOL_LEN,
right?

Sure >320 chars long kfunc name is unlikely, but technically possible.

> 
>> +	const struct btf_type *func;
>> +	s32 impl_id;
>> +	int len;
>> +
>> +	len = snprintf(impl_name, sizeof(impl_name), "%s%s", func_name, KF_IMPL_SUFFIX);
>> +	if (len < 0 || len >= sizeof(impl_name)) {
>> +		verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
>> +		return NULL;
>> +	}
> 
> [...]
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Eduard Zingerman 3 weeks, 4 days ago
On Tue, 2026-01-13 at 16:03 -0800, Ihor Solodrai wrote:
> On 1/13/26 1:59 PM, Eduard Zingerman wrote:
> > On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> > 
> > [...]
> > 
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
> > >  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
> > >  }
> > >  
> > > +#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)
> > > +{
> > > +	char impl_name[KSYM_SYMBOL_LEN];
> > 
> > Oh, as we discussed already, this should use env->tmp_str_buf.
> 
> The env->tmp_str_buf size is smaller:
> 
> 	#define TMP_STR_BUF_LEN 320
> 
> *And* there is already a local char buffer of size KSYM_SYMBOL_LEN
> already in use in verifier.c:
> 
> 	int bpf_check_attach_target(...) {
> 		bool prog_extension = prog->type == BPF_PROG_TYPE_EXT;
> 		bool prog_tracing = prog->type == BPF_PROG_TYPE_TRACING;
> 		char trace_symbol[KSYM_SYMBOL_LEN];
> 	[...]
> 
> Since these are function names, the real limit is KSYM_SYMBOL_LEN,
> right?
> 
> Sure >320 chars long kfunc name is unlikely, but technically possible.

320 is good enough, you'll be able to cover this:

kfunc_trace_long_descriptive_kernel_symbol_for_tracing_scheduler_memory_io_and_interrupt_paths_during_runtime_analysis_of_latency_throughput_and_resource_contention_on_large_scale_multiprocessor_linux_systems_using_bpf_and_kprobes_without_requiring_kernel_recompilation_or_system_restart_for_production_use_cases_v2x

But not this:

kfunc_trace_kernel_scheduler_and_memory_management_path_for_observing_task_lifecycle_events_context_switches_page_fault_handling_and_io_wait_states_while_debugging_performance_regressions_on_large_multiprocessor_systems_running_preemptible_linux_kernels_with_bpf_tracing_and_dynamic_instrumentation_enabled_for_deep_visibility_into_runtime_behavior_and_latency_sensitive_code_paths_without_recompilation.

Should suffice, I think.
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 3 weeks, 4 days ago

On 1/13/26 5:06 PM, Eduard Zingerman wrote:
> On Tue, 2026-01-13 at 16:03 -0800, Ihor Solodrai wrote:
>> On 1/13/26 1:59 PM, Eduard Zingerman wrote:
>>> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>>>
>>> [...]
>>>
>>>> --- a/kernel/bpf/verifier.c
>>>> +++ b/kernel/bpf/verifier.c
>>>> @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
>>>>  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
>>>>  }
>>>>  
>>>> +#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)
>>>> +{
>>>> +	char impl_name[KSYM_SYMBOL_LEN];
>>>
>>> Oh, as we discussed already, this should use env->tmp_str_buf.
>>
>> The env->tmp_str_buf size is smaller:
>>
>> 	#define TMP_STR_BUF_LEN 320
>>
>> *And* there is already a local char buffer of size KSYM_SYMBOL_LEN
>> already in use in verifier.c:
>>
>> 	int bpf_check_attach_target(...) {
>> 		bool prog_extension = prog->type == BPF_PROG_TYPE_EXT;
>> 		bool prog_tracing = prog->type == BPF_PROG_TYPE_TRACING;
>> 		char trace_symbol[KSYM_SYMBOL_LEN];
>> 	[...]
>>
>> Since these are function names, the real limit is KSYM_SYMBOL_LEN,
>> right?
>>
>> Sure >320 chars long kfunc name is unlikely, but technically possible.
> 
> 320 is good enough, you'll be able to cover this:
> 
> kfunc_trace_long_descriptive_kernel_symbol_for_tracing_scheduler_memory_io_and_interrupt_paths_during_runtime_analysis_of_latency_throughput_and_resource_contention_on_large_scale_multiprocessor_linux_systems_using_bpf_and_kprobes_without_requiring_kernel_recompilation_or_system_restart_for_production_use_cases_v2x
> 
> But not this:
> 
> kfunc_trace_kernel_scheduler_and_memory_management_path_for_observing_task_lifecycle_events_context_switches_page_fault_handling_and_io_wait_states_while_debugging_performance_regressions_on_large_multiprocessor_systems_running_preemptible_linux_kernels_with_bpf_tracing_and_dynamic_instrumentation_enabled_for_deep_visibility_into_runtime_behavior_and_latency_sensitive_code_paths_without_recompilation.
> 
> Should suffice, I think.

I will laugh for at least 321 seconds when the size of this buffer
will have to be increased, and will make sure you hear it :)

They thought 640K of memory is enough, you know.
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Eduard Zingerman 3 weeks, 4 days ago
On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
> expected to have two associated types in BTF:
>   * `bpf_foo` with a function prototype that omits implicit arguments
>   * `bpf_foo_impl` with a function prototype that matches the kernel
>      declaration of `bpf_foo`, but doesn't have a ksym associated with
>      its name
> 
> In order to support kfuncs with implicit arguments, the verifier has
> to know how to resolve a call of `bpf_foo` to the correct BTF function
> prototype and address.
> 
> To implement this, in add_kfunc_call() kfunc flags are checked for
> KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
> the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
> convention) function in BTF.
> 
> This effectively changes the signature of the `bpf_foo` kfunc in the
> context of verification: from one without implicit args to the one
> with full argument list.
> 
> Whether a kfunc argument is implicit or not is determined by
> is_kfunc_arg_implicit(). The values of implicit arguments by design
> are provided by the verifier, and so they can only be of particular
> types. In this patch the only allowed implicit arg type is a pointer
> to struct bpf_prog_aux. The __prog args (usually void *) are also
> considered implicit for backwards compatibility.
> 
> In order to enable the verifier to correctly set an implicit
> bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
> check for the arg type. At a point when prog arg is determined in
> check_kfunc_args() the kfunc with implicit args already has a
> prototype with full argument list, so the existing value patch
> mechanism just works.
> 
> If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
> that uses a __prog argument (a legacy case), the prototype
> substitution works in exactly the same way, assuming the kfunc follows
> the _impl naming convention. The difference is only in how _impl
> prototype is added to the BTF, which is not the verifier's
> concern. See a subsequent resolve_btfids patch for details.
> 
> In check_kfunc_call() reset the subreg_def of registers holding
> implicit arguments to correctly track zero extensions.
> 
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---

Overall lgtm.

[...]

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c

[...]

> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>  	for (i = 0; i < nargs; i++) {
>  		u32 regno = i + 1;
>  
> +		/*
> +		 * Implicit kfunc arguments are set after main verification pass.
> +		 * For correct tracking of zero-extensions we have to reset subreg_def for such
> +		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
> +		 * from an earlier (irrelevant) point in the program, which may lead to an error
> +		 * in opt_subreg_zext_lo32_rnd_hi32().
> +		 */
> +		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> +				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
> +			regs[regno].subreg_def = DEF_NOT_SUBREG;
> +

Did you try doing this in `mark_reg_not_init()`?
This function is called for R1-R5 some time prior this hunk.
What I don't like from structural point of view is:
- `is_kfunc_arg_implicit()` depends on KF_IMPLICIT_ARGS, but that
  check is done externally. Hence, the naming is misleading or 'meta'
  should be passed to `is_kfunc_arg_implicit()`.
- doing DEF_NOT_SUBREG logically has not much to do with implicit args,
  so it is a bit confusing that is pre-conditioned like that.

>  		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
>  		if (btf_type_is_ptr(t))
>  			mark_btf_func_reg_size(env, regno, sizeof(void *));
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 3 weeks, 4 days ago
On 1/13/26 12:39 PM, Eduard Zingerman wrote:
> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>> A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
>> expected to have two associated types in BTF:
>>   * `bpf_foo` with a function prototype that omits implicit arguments
>>   * `bpf_foo_impl` with a function prototype that matches the kernel
>>      declaration of `bpf_foo`, but doesn't have a ksym associated with
>>      its name
>>
>> In order to support kfuncs with implicit arguments, the verifier has
>> to know how to resolve a call of `bpf_foo` to the correct BTF function
>> prototype and address.
>>
>> To implement this, in add_kfunc_call() kfunc flags are checked for
>> KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
>> the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
>> convention) function in BTF.
>>
>> This effectively changes the signature of the `bpf_foo` kfunc in the
>> context of verification: from one without implicit args to the one
>> with full argument list.
>>
>> Whether a kfunc argument is implicit or not is determined by
>> is_kfunc_arg_implicit(). The values of implicit arguments by design
>> are provided by the verifier, and so they can only be of particular
>> types. In this patch the only allowed implicit arg type is a pointer
>> to struct bpf_prog_aux. The __prog args (usually void *) are also
>> considered implicit for backwards compatibility.
>>
>> In order to enable the verifier to correctly set an implicit
>> bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
>> check for the arg type. At a point when prog arg is determined in
>> check_kfunc_args() the kfunc with implicit args already has a
>> prototype with full argument list, so the existing value patch
>> mechanism just works.
>>
>> If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
>> that uses a __prog argument (a legacy case), the prototype
>> substitution works in exactly the same way, assuming the kfunc follows
>> the _impl naming convention. The difference is only in how _impl
>> prototype is added to the BTF, which is not the verifier's
>> concern. See a subsequent resolve_btfids patch for details.
>>
>> In check_kfunc_call() reset the subreg_def of registers holding
>> implicit arguments to correctly track zero extensions.
>>
>> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
>> ---
> 
> Overall lgtm.
> 
> [...]
> 
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> 
> [...]
> 
>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>  	for (i = 0; i < nargs; i++) {
>>  		u32 regno = i + 1;
>>  
>> +		/*
>> +		 * Implicit kfunc arguments are set after main verification pass.
>> +		 * For correct tracking of zero-extensions we have to reset subreg_def for such
>> +		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
>> +		 * from an earlier (irrelevant) point in the program, which may lead to an error
>> +		 * in opt_subreg_zext_lo32_rnd_hi32().
>> +		 */
>> +		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
>> +				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
>> +			regs[regno].subreg_def = DEF_NOT_SUBREG;
>> +
> 
> Did you try doing this in `mark_reg_not_init()`?
> This function is called for R1-R5 some time prior this hunk.

> Did you try doing this in `mark_reg_not_init()`?

Just tried, it doesn't work because REG0 is considered a caller saved
register, and so it breaks the zext tracking:

        #define CALLER_SAVED_REGS 6
        static const int caller_saved[CALLER_SAVED_REGS] = {
	     BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
        };

        [...]

	for (i = 0; i < CALLER_SAVED_REGS; i++)
		mark_reg_not_init(env, regs, caller_saved[i]);

CI run for the diff below (on top of this series):
https://github.com/kernel-patches/bpf/actions/runs/20972520708


diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b4e40b87e8fa..8bbcd1466815 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2784,6 +2784,8 @@ static void __reg_assign_32_into_64(struct bpf_reg_state *reg)
        }
 }
 
+#define DEF_NOT_SUBREG (0)
+
 /* Mark a register as having a completely unknown (scalar) value. */
 static void __mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
 {
@@ -2798,6 +2800,7 @@ static void __mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
        reg->var_off = tnum_unknown;
        reg->frameno = 0;
        reg->precise = false;
+       reg->subreg_def = DEF_NOT_SUBREG;
        __mark_reg_unbounded(reg);
 }
 
@@ -2892,7 +2895,6 @@ static int mark_btf_ld_reg(struct bpf_verifier_env *env,
        }
 }
 
-#define DEF_NOT_SUBREG (0)
 static void init_reg_state(struct bpf_verifier_env *env,
                           struct bpf_func_state *state)
 {
@@ -14363,17 +14365,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
        for (i = 0; i < nargs; i++) {
                u32 regno = i + 1;
 
-               /*
-                * Implicit kfunc arguments are set after main verification pass.
-                * For correct tracking of zero-extensions we have to reset subreg_def for such
-                * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
-                * from an earlier (irrelevant) point in the program, which may lead to an error
-                * in opt_subreg_zext_lo32_rnd_hi32().
-                */
-               if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
-                               && is_kfunc_arg_implicit(desc_btf, &args[i])))
-                       regs[regno].subreg_def = DEF_NOT_SUBREG;
-
                t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
                if (btf_type_is_ptr(t))
                        mark_btf_func_reg_size(env, regno, sizeof(void *));

---

Resetting all reg args appears to be working however (see below).
CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221

Should I send this as a separate patch?

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8bbcd1466815..9dfcf3149841 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2800,7 +2800,6 @@ static void __mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
        reg->var_off = tnum_unknown;
        reg->frameno = 0;
        reg->precise = false;
-       reg->subreg_def = DEF_NOT_SUBREG;
        __mark_reg_unbounded(reg);
 }
 
@@ -14241,6 +14240,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
        for (i = 0; i < CALLER_SAVED_REGS; i++)
                mark_reg_not_init(env, regs, caller_saved[i]);
 
+       for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
+               u32 regno = i + 1;
+               regs[regno].subreg_def = DEF_NOT_SUBREG;
+       }
+
        /* Check return type */
        t = btf_type_skip_modifiers(desc_btf, meta.func_proto->type, NULL);


> What I don't like from structural point of view is:
> - `is_kfunc_arg_implicit()` depends on KF_IMPLICIT_ARGS, but that
>   check is done externally. Hence, the naming is misleading or 'meta'
>   should be passed to `is_kfunc_arg_implicit()`.
> - doing DEF_NOT_SUBREG logically has not much to do with implicit args,
>   so it is a bit confusing that is pre-conditioned like that.
> 
>>  		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
>>  		if (btf_type_is_ptr(t))
>>  			mark_btf_func_reg_size(env, regno, sizeof(void *));
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 3 weeks, 4 days ago
On 1/13/26 2:03 PM, Ihor Solodrai wrote:
> On 1/13/26 12:39 PM, Eduard Zingerman wrote:
>> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>>> 
>>
>> [...]
>>
>>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>>  	for (i = 0; i < nargs; i++) {
>>>  		u32 regno = i + 1;
>>>  
>>> +		/*
>>> +		 * Implicit kfunc arguments are set after main verification pass.
>>> +		 * For correct tracking of zero-extensions we have to reset subreg_def for such
>>> +		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
>>> +		 * from an earlier (irrelevant) point in the program, which may lead to an error
>>> +		 * in opt_subreg_zext_lo32_rnd_hi32().
>>> +		 */
>>> +		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
>>> +				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
>>> +			regs[regno].subreg_def = DEF_NOT_SUBREG;
>>> +
>>
>> Did you try doing this in `mark_reg_not_init()`?
>> This function is called for R1-R5 some time prior this hunk.
> 
>> Did you try doing this in `mark_reg_not_init()`?
> 
> Just tried, it doesn't work because REG0 is considered a caller saved
> register, and so it breaks the zext tracking:
> 
>         #define CALLER_SAVED_REGS 6
>         static const int caller_saved[CALLER_SAVED_REGS] = {
> 	     BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
>         };
> 
>         [...]
> 
> 	for (i = 0; i < CALLER_SAVED_REGS; i++)
> 		mark_reg_not_init(env, regs, caller_saved[i]);
> 
> CI run for the diff below (on top of this series):
> https://github.com/kernel-patches/bpf/actions/runs/20972520708
> 
>
> [...]
> 
> ---
> 
> Resetting all reg args appears to be working however (see below).
> CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221
> 

A follow up after a chat with Eduard.

This change in check_kfunc_call() appears to be working:

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 092003cc7841..ff743335111c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
                regs = branch->frame[branch->curframe]->regs;
 
                /* Clear r0-r5 registers in forked state */
-               for (i = 0; i < CALLER_SAVED_REGS; i++)
-                       mark_reg_not_init(env, regs, caller_saved[i]);
+               for (i = 0; i < CALLER_SAVED_REGS; i++) {
+                       u32 regno = caller_saved[i];
+                       mark_reg_not_init(env, regs, regno);
+                       regs[regno].subreg_def = DEF_NOT_SUBREG;
+               }
 
                mark_reg_unknown(env, regs, BPF_REG_0);
                err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);

https://github.com/kernel-patches/bpf/actions/runs/20975419422

Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
breaks zero-extension tracking somewhere else.  But this is not
directly relevant to the series.

Eduard, Alexei, any concerns with this diff? Should I send a separate
patch?


>  [...]
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Eduard Zingerman 3 weeks, 4 days ago
On Tue, 2026-01-13 at 15:48 -0800, Ihor Solodrai wrote:

[...]

> A follow up after a chat with Eduard.
> 
> This change in check_kfunc_call() appears to be working:
> 
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 092003cc7841..ff743335111c 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>                 regs = branch->frame[branch->curframe]->regs;
>  
>                 /* Clear r0-r5 registers in forked state */
> -               for (i = 0; i < CALLER_SAVED_REGS; i++)
> -                       mark_reg_not_init(env, regs, caller_saved[i]);
> +               for (i = 0; i < CALLER_SAVED_REGS; i++) {
> +                       u32 regno = caller_saved[i];
> +                       mark_reg_not_init(env, regs, regno);
> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;
> +               }
>  
>                 mark_reg_unknown(env, regs, BPF_REG_0);
>                 err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);
> 
> https://github.com/kernel-patches/bpf/actions/runs/20975419422
> 
> Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
> breaks zero-extension tracking somewhere else.  But this is not
> directly relevant to the series.
> 
> Eduard, Alexei, any concerns with this diff? Should I send a separate
> patch?

Imo this is acceptable to land this series but follow up investigation
is definitely needed. Either there is a bug and mark_reg_not_init() is
called in a context where upper 32-bits are still significant, or zero
extension related code can be improved to avoid patching in some cases.

Additional context for other reviewers, Ihor did two experiments:
- added '.subreg_def = DEF_NOT_SUBREG' to mark_reg_not_init(),
  which resulted in selftests failure;
- added '.subreg_def = DEF_NOT_SUBREG' as above, which worked fine.

Meaning that code in check_kfunc_call() is not a culprit.
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Alexei Starovoitov 3 weeks, 4 days ago
On Tue, Jan 13, 2026 at 3:48 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/13/26 2:03 PM, Ihor Solodrai wrote:
> > On 1/13/26 12:39 PM, Eduard Zingerman wrote:
> >> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> >>>
> >>
> >> [...]
> >>
> >>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> >>>     for (i = 0; i < nargs; i++) {
> >>>             u32 regno = i + 1;
> >>>
> >>> +           /*
> >>> +            * Implicit kfunc arguments are set after main verification pass.
> >>> +            * For correct tracking of zero-extensions we have to reset subreg_def for such
> >>> +            * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
> >>> +            * from an earlier (irrelevant) point in the program, which may lead to an error
> >>> +            * in opt_subreg_zext_lo32_rnd_hi32().
> >>> +            */
> >>> +           if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> >>> +                           && is_kfunc_arg_implicit(desc_btf, &args[i])))
> >>> +                   regs[regno].subreg_def = DEF_NOT_SUBREG;
> >>> +
> >>
> >> Did you try doing this in `mark_reg_not_init()`?
> >> This function is called for R1-R5 some time prior this hunk.
> >
> >> Did you try doing this in `mark_reg_not_init()`?
> >
> > Just tried, it doesn't work because REG0 is considered a caller saved
> > register, and so it breaks the zext tracking:
> >
> >         #define CALLER_SAVED_REGS 6
> >         static const int caller_saved[CALLER_SAVED_REGS] = {
> >            BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
> >         };
> >
> >         [...]
> >
> >       for (i = 0; i < CALLER_SAVED_REGS; i++)
> >               mark_reg_not_init(env, regs, caller_saved[i]);
> >
> > CI run for the diff below (on top of this series):
> > https://github.com/kernel-patches/bpf/actions/runs/20972520708
> >
> >
> > [...]
> >
> > ---
> >
> > Resetting all reg args appears to be working however (see below).
> > CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221
> >
>
> A follow up after a chat with Eduard.
>
> This change in check_kfunc_call() appears to be working:
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 092003cc7841..ff743335111c 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>                 regs = branch->frame[branch->curframe]->regs;
>
>                 /* Clear r0-r5 registers in forked state */
> -               for (i = 0; i < CALLER_SAVED_REGS; i++)
> -                       mark_reg_not_init(env, regs, caller_saved[i]);
> +               for (i = 0; i < CALLER_SAVED_REGS; i++) {
> +                       u32 regno = caller_saved[i];
> +                       mark_reg_not_init(env, regs, regno);
> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;
> +               }
>
>                 mark_reg_unknown(env, regs, BPF_REG_0);
>                 err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);
>
> https://github.com/kernel-patches/bpf/actions/runs/20975419422
>
> Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
> breaks zero-extension tracking somewhere else.  But this is not
> directly relevant to the series.
>
> Eduard, Alexei, any concerns with this diff? Should I send a separate
> patch?

This is odd. Clear it only for res_spin_lock() processing?!
Should be around lines 14149 instead?

First, need to investigate why clearing it in mark_reg_not_init()
breaks things.
That's what clear_caller_saved_regs() is doing already.
Maybe these two loops in check_kfunc_call() should be doing
clear_caller_saved_regs() instead...
Needs proper investigation.
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Ihor Solodrai 3 weeks, 4 days ago

On 1/13/26 4:55 PM, Alexei Starovoitov wrote:
> On Tue, Jan 13, 2026 at 3:48 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> On 1/13/26 2:03 PM, Ihor Solodrai wrote:
>>> On 1/13/26 12:39 PM, Eduard Zingerman wrote:
>>>> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>>>>>
>>>>
>>>> [...]
>>>>
>>>>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>>>>     for (i = 0; i < nargs; i++) {
>>>>>             u32 regno = i + 1;
>>>>>
>>>>> +           /*
>>>>> +            * Implicit kfunc arguments are set after main verification pass.
>>>>> +            * For correct tracking of zero-extensions we have to reset subreg_def for such
>>>>> +            * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
>>>>> +            * from an earlier (irrelevant) point in the program, which may lead to an error
>>>>> +            * in opt_subreg_zext_lo32_rnd_hi32().
>>>>> +            */
>>>>> +           if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
>>>>> +                           && is_kfunc_arg_implicit(desc_btf, &args[i])))
>>>>> +                   regs[regno].subreg_def = DEF_NOT_SUBREG;
>>>>> +
>>>>
>>>> Did you try doing this in `mark_reg_not_init()`?
>>>> This function is called for R1-R5 some time prior this hunk.
>>>
>>>> Did you try doing this in `mark_reg_not_init()`?
>>>
>>> Just tried, it doesn't work because REG0 is considered a caller saved
>>> register, and so it breaks the zext tracking:
>>>
>>>         #define CALLER_SAVED_REGS 6
>>>         static const int caller_saved[CALLER_SAVED_REGS] = {
>>>            BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
>>>         };
>>>
>>>         [...]
>>>
>>>       for (i = 0; i < CALLER_SAVED_REGS; i++)
>>>               mark_reg_not_init(env, regs, caller_saved[i]);
>>>
>>> CI run for the diff below (on top of this series):
>>> https://github.com/kernel-patches/bpf/actions/runs/20972520708
>>>
>>>
>>> [...]
>>>
>>> ---
>>>
>>> Resetting all reg args appears to be working however (see below).
>>> CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221
>>>
>>
>> A follow up after a chat with Eduard.
>>
>> This change in check_kfunc_call() appears to be working:
>>
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 092003cc7841..ff743335111c 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>                 regs = branch->frame[branch->curframe]->regs;
>>
>>                 /* Clear r0-r5 registers in forked state */
>> -               for (i = 0; i < CALLER_SAVED_REGS; i++)
>> -                       mark_reg_not_init(env, regs, caller_saved[i]);
>> +               for (i = 0; i < CALLER_SAVED_REGS; i++) {
>> +                       u32 regno = caller_saved[i];
>> +                       mark_reg_not_init(env, regs, regno);
>> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;
>> +               }
>>
>>                 mark_reg_unknown(env, regs, BPF_REG_0);
>>                 err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);
>>
>> https://github.com/kernel-patches/bpf/actions/runs/20975419422
>>
>> Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
>> breaks zero-extension tracking somewhere else.  But this is not
>> directly relevant to the series.
>>
>> Eduard, Alexei, any concerns with this diff? Should I send a separate
>> patch?
> 
> This is odd. Clear it only for res_spin_lock() processing?!
> Should be around lines 14149 instead?

Yes. Sorry, this was a messed up local diff. The commits tested on CI
are correct though. I'll have this fix in v2, since it is necessary
for KF_IMPLICIT_ARGS to work.

I'll look into this problem more after implicit args land, unless
someone beats me to it.

> 
> First, need to investigate why clearing it in mark_reg_not_init()
> breaks things.
> That's what clear_caller_saved_regs() is doing already.
> Maybe these two loops in check_kfunc_call() should be doing
> clear_caller_saved_regs() instead...
> Needs proper investigation.

Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Andrii Nakryiko 4 weeks, 1 day ago
On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
> expected to have two associated types in BTF:
>   * `bpf_foo` with a function prototype that omits implicit arguments
>   * `bpf_foo_impl` with a function prototype that matches the kernel
>      declaration of `bpf_foo`, but doesn't have a ksym associated with
>      its name
>
> In order to support kfuncs with implicit arguments, the verifier has
> to know how to resolve a call of `bpf_foo` to the correct BTF function
> prototype and address.
>
> To implement this, in add_kfunc_call() kfunc flags are checked for
> KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
> the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
> convention) function in BTF.
>
> This effectively changes the signature of the `bpf_foo` kfunc in the
> context of verification: from one without implicit args to the one
> with full argument list.
>
> Whether a kfunc argument is implicit or not is determined by
> is_kfunc_arg_implicit(). The values of implicit arguments by design
> are provided by the verifier, and so they can only be of particular
> types. In this patch the only allowed implicit arg type is a pointer
> to struct bpf_prog_aux. The __prog args (usually void *) are also
> considered implicit for backwards compatibility.
>
> In order to enable the verifier to correctly set an implicit
> bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
> check for the arg type. At a point when prog arg is determined in
> check_kfunc_args() the kfunc with implicit args already has a
> prototype with full argument list, so the existing value patch
> mechanism just works.
>
> If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
> that uses a __prog argument (a legacy case), the prototype
> substitution works in exactly the same way, assuming the kfunc follows
> the _impl naming convention. The difference is only in how _impl
> prototype is added to the BTF, which is not the verifier's
> concern. See a subsequent resolve_btfids patch for details.
>
> In check_kfunc_call() reset the subreg_def of registers holding
> implicit arguments to correctly track zero extensions.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  include/linux/btf.h   |  1 +
>  kernel/bpf/verifier.c | 70 +++++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 69 insertions(+), 2 deletions(-)
>

[...]

> +       impl_id = btf_find_by_name_kind(btf, impl_name, BTF_KIND_FUNC);
> +       if (impl_id <= 0) {
> +               verbose(env, "cannot find function %s in BTF\n", impl_name);
> +               return NULL;
> +       }
> +
> +       func = btf_type_by_id(btf, impl_id);
> +       if (!func || !btf_type_is_func(func)) {

btf_find_by_name_kind() above guarantees that we both will have
non-NULL func and it will be BTF_KIND_FUNC, drop these defensive
checks.

> +               verbose(env, "%s (btf_id %d) is not a function\n", impl_name, impl_id);
> +               return NULL;
> +       }
> +
> +       return btf_type_by_id(btf, func->type);
> +}
> +
>  static int fetch_kfunc_meta(struct bpf_verifier_env *env,
>                             s32 func_id,
>                             s16 offset,
> @@ -3308,7 +3340,16 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
>         }
>
>         func_name = btf_name_by_offset(btf, func->name_off);
> -       func_proto = btf_type_by_id(btf, func->type);
> +
> +       /*
> +        * An actual prototype of a kfunc with KF_IMPLICIT_ARGS flag
> +        * can be found through the counterpart _impl kfunc.
> +        */
> +       if (unlikely(kfunc_flags && KF_IMPLICIT_ARGS & *kfunc_flags))

drop unlikely(), it's unnecessary micro-optimization (if at all)

(I'd also swap order to more conventional: `*kfunc_flags & KF_IMPLICIT_ARGS`)

> +               func_proto = find_kfunc_impl_proto(env, btf, func_name);
> +       else
> +               func_proto = btf_type_by_id(btf, func->type);
> +
>         if (!func_proto || !btf_type_is_func_proto(func_proto)) {
>                 verbose(env, "kernel function btf_id %d does not have a valid func_proto\n",
>                         func_id);

[...]

> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>         for (i = 0; i < nargs; i++) {
>                 u32 regno = i + 1;
>
> +               /*
> +                * Implicit kfunc arguments are set after main verification pass.
> +                * For correct tracking of zero-extensions we have to reset subreg_def for such
> +                * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
> +                * from an earlier (irrelevant) point in the program, which may lead to an error
> +                * in opt_subreg_zext_lo32_rnd_hi32().
> +                */
> +               if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> +                               && is_kfunc_arg_implicit(desc_btf, &args[i])))
> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;

ditto about unlikely(), this is used for rare cases where performance
matters a lot (and it's obvious which case is "common", so should be
kept linear in assembly code)


> +
>                 t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
>                 if (btf_type_is_ptr(t))
>                         mark_btf_func_reg_size(env, regno, sizeof(void *));
> --
> 2.52.0
>
Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
Posted by Alexei Starovoitov 4 weeks, 1 day ago
On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> +       if (unlikely(kfunc_flags && KF_IMPLICIT_ARGS & *kfunc_flags))

Please add extra () around &

that's the style we use elsewhere. Like:
if (!first && (env->log.level & BPF_LOG_LEVEL2))

> +               if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> +                               && is_kfunc_arg_implicit(desc_btf, &args[i])))

same

pw-bot: cr