* [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages
2026-04-18 17:16 [PATCH bpf-next v3 0/4] Add support to emit verifier warnings Kumar Kartikeya Dwivedi
@ 2026-04-18 17:16 ` Kumar Kartikeya Dwivedi
2026-04-18 17:42 ` sashiko-bot
2026-04-20 13:37 ` Mykyta Yatsenko
2026-04-18 17:16 ` [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation Kumar Kartikeya Dwivedi
` (2 subsequent siblings)
3 siblings, 2 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 17:16 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
Add a mode where log_level 16 can be used to receive warnings and have
an associated log buffer. Add a warn() macro that emits messages to log
buffer without any restrictions, aggregate the warnings emitted, and
then use it to decide whether we reset the log or not.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
Documentation/bpf/kfuncs.rst | 4 +++-
include/linux/bpf_verifier.h | 11 +++++++++--
kernel/bpf/log.c | 2 +-
kernel/bpf/verifier.c | 21 +++++++++++++++++++--
4 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 75e6c078e0e7..47d60011d8eb 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -353,7 +353,9 @@ marked with KF_DEPRECATED should also have any relevant information
captured in its kernel doc. Such information typically includes the
kfunc's expected remaining lifespan, a recommendation for new
functionality that can replace it if any is available, and possibly a
-rationale for why it is being removed.
+rationale for why it is being removed. When verifier warning logging is
+requested, the verifier will emit a warning when a BPF program uses a
+deprecated kfunc.
Note that while on some occasions, a KF_DEPRECATED kfunc may continue to be
supported and have its KF_DEPRECATED flag removed, it is likely to be far more
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index b148f816f25b..515afcb83ec7 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -689,15 +689,21 @@ struct bpf_verifier_log {
#define BPF_LOG_LEVEL2 2
#define BPF_LOG_STATS 4
#define BPF_LOG_FIXED 8
+#define BPF_LOG_LEVEL_WARN 16
#define BPF_LOG_LEVEL (BPF_LOG_LEVEL1 | BPF_LOG_LEVEL2)
-#define BPF_LOG_MASK (BPF_LOG_LEVEL | BPF_LOG_STATS | BPF_LOG_FIXED)
+#define BPF_LOG_MASK (BPF_LOG_LEVEL | BPF_LOG_STATS | BPF_LOG_FIXED | BPF_LOG_LEVEL_WARN)
#define BPF_LOG_KERNEL (BPF_LOG_MASK + 1) /* kernel internal flag */
#define BPF_LOG_MIN_ALIGNMENT 8U
#define BPF_LOG_ALIGNMENT 40U
static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
{
- return log && log->level;
+ return log && (log->level & ~BPF_LOG_LEVEL_WARN);
+}
+
+static inline bool bpf_verifier_warn_needed(const struct bpf_verifier_log *log)
+{
+ return log && (log->level & BPF_LOG_LEVEL_WARN);
}
#define BPF_MAX_SUBPROGS 256
@@ -848,6 +854,7 @@ struct bpf_verifier_env {
bool bypass_spec_v4;
bool seen_direct_write;
bool seen_exception;
+ bool warnings;
struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
const struct bpf_line_info *prev_linfo;
struct bpf_verifier_log log;
diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
index 011e4ec25acd..2cf79fac8d43 100644
--- a/kernel/bpf/log.c
+++ b/kernel/bpf/log.c
@@ -154,7 +154,7 @@ void bpf_vlog_reset(struct bpf_verifier_log *log, u64 new_pos)
if (WARN_ON_ONCE(new_pos > log->end_pos))
return;
- if (!bpf_verifier_log_needed(log) || log->level == BPF_LOG_KERNEL)
+ if (!log || log->level == 0 || log->level == BPF_LOG_KERNEL)
return;
/* if position to which we reset is beyond current log window,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 69d75515ed3f..8de2a4e5f5de 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -47,6 +47,7 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
enum bpf_features {
BPF_FEAT_RDONLY_CAST_TO_VOID = 0,
BPF_FEAT_STREAMS = 1,
+ BPF_FEAT_VERIFIER_WARNINGS = 2,
__MAX_BPF_FEAT,
};
@@ -282,6 +283,20 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
va_end(args);
}
+__printf(2, 3) static void warn(void *private_data, const char *fmt, ...)
+{
+ struct bpf_verifier_env *env = private_data;
+ va_list args;
+
+ if (!bpf_verifier_warn_needed(&env->log))
+ return;
+
+ va_start(args, fmt);
+ bpf_verifier_vlog(&env->log, fmt, args);
+ va_end(args);
+ env->warnings = true;
+}
+
static void verbose_invalid_scalar(struct bpf_verifier_env *env,
struct bpf_reg_state *reg,
struct bpf_retval_range range, const char *ctx,
@@ -1683,7 +1698,8 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
if (err)
return err;
}
- if (pop_log)
+ /* Preserve warning output across branch explorations. */
+ if (pop_log && !(env->warnings && bpf_verifier_warn_needed(&env->log)))
bpf_vlog_reset(&env->log, head->log_pos);
if (insn_idx)
*insn_idx = head->insn_idx;
@@ -18803,7 +18819,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
ret = do_check(env);
out:
- if (!ret && pop_log)
+ if (!ret && pop_log &&
+ !(env->warnings && bpf_verifier_warn_needed(&env->log)))
bpf_vlog_reset(&env->log, 0);
free_states(env);
return ret;
--
2.52.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages
2026-04-18 17:16 ` [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages Kumar Kartikeya Dwivedi
@ 2026-04-18 17:42 ` sashiko-bot
2026-04-18 20:33 ` Kumar Kartikeya Dwivedi
2026-04-20 13:37 ` Mykyta Yatsenko
1 sibling, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-04-18 17:42 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi; +Cc: bpf
Sashiko AI review found 2 potential issue(s):
- [High] Unused static function `warn()` breaks the build and bisection.
- [High] Disabling log reset completely after a warning leads to log buffer explosion and overwrites the warning when combined with `BPF_LOG_LEVEL1`.
--
commit 35e5ff3f696c81863084f9e6fb58225f8242f3e0
Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
bpf: Add support for verifier warning messages
This commit adds a mode where log_level 16 can be used to receive warnings
and have an associated log buffer. It introduces a warn function and
aggregates warnings to decide whether to reset the log.
> Add a warn() macro that emits messages to log
> buffer without any restrictions, aggregate the warnings emitted, and
This isn't a bug, but the commit message states it adds a warn() macro
when a static function is actually added.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 69d75515ed3f7..8de2a4e5f5de2 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -282,6 +283,20 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
> va_end(args);
> }
>
> +__printf(2, 3) static void warn(void *private_data, const char *fmt, ...)
> +{
> + struct bpf_verifier_env *env = private_data;
> + va_list args;
> +
> + if (!bpf_verifier_warn_needed(&env->log))
> + return;
> +
> + va_start(args, fmt);
> + bpf_verifier_vlog(&env->log, fmt, args);
> + va_end(args);
> + env->warnings = true;
> +}
Is this static function called anywhere in this patch?
Unused static functions trigger a -Wunused-function compiler warning, which
can break builds when CONFIG_WERROR is enabled.
[ ... ]
> @@ -1683,7 +1698,8 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
> if (err)
> return err;
> }
> - if (pop_log)
> + /* Preserve warning output across branch explorations. */
> + if (pop_log && !(env->warnings && bpf_verifier_warn_needed(&env->log)))
> bpf_vlog_reset(&env->log, head->log_pos);
> if (insn_idx)
> *insn_idx = head->insn_idx;
Can this completely disable log resets once a warning is emitted?
If a user requests both standard logging and warning logging, both verbose()
and warn() will actively write to the log buffer.
If log pruning is permanently disabled after the first warning, will the
verifier dump the state of every successfully verified instruction into the
log for all subsequent branch explorations?
Because the log buffer acts as a rotating buffer by default, could this
flood of output wrap around and overwrite the warning that was meant to be
preserved?
Would it be better to track the log position of the warning and reset to
the maximum of head->log_pos and the warning position?
[ ... ]
> @@ -18803,7 +18819,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
>
> ret = do_check(env);
> out:
> - if (!ret && pop_log)
> + if (!ret && pop_log &&
> + !(env->warnings && bpf_verifier_warn_needed(&env->log)))
> bpf_vlog_reset(&env->log, 0);
> free_states(env);
> return ret;
Could the same log buffer wrapping issue happen here when log resets are
disabled after a warning?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=1
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages
2026-04-18 17:42 ` sashiko-bot
@ 2026-04-18 20:33 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 20:33 UTC (permalink / raw)
To: sashiko; +Cc: bpf
On Sat, 18 Apr 2026 at 18:42, <sashiko-bot@kernel.org> wrote:
>
> Sashiko AI review found 2 potential issue(s):
> - [High] Unused static function `warn()` breaks the build and bisection.
> - [High] Disabling log reset completely after a warning leads to log buffer explosion and overwrites the warning when combined with `BPF_LOG_LEVEL1`.
> --
>
> commit 35e5ff3f696c81863084f9e6fb58225f8242f3e0
> Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>
> bpf: Add support for verifier warning messages
>
> This commit adds a mode where log_level 16 can be used to receive warnings
> and have an associated log buffer. It introduces a warn function and
> aggregates warnings to decide whether to reset the log.
>
> > Add a warn() macro that emits messages to log
> > buffer without any restrictions, aggregate the warnings emitted, and
>
> This isn't a bug, but the commit message states it adds a warn() macro
> when a static function is actually added.
Will fix.
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 69d75515ed3f7..8de2a4e5f5de2 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -282,6 +283,20 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
> > va_end(args);
> > }
> >
> > +__printf(2, 3) static void warn(void *private_data, const char *fmt, ...)
> > +{
> > + struct bpf_verifier_env *env = private_data;
> > + va_list args;
> > +
> > + if (!bpf_verifier_warn_needed(&env->log))
> > + return;
> > +
> > + va_start(args, fmt);
> > + bpf_verifier_vlog(&env->log, fmt, args);
> > + va_end(args);
> > + env->warnings = true;
> > +}
>
> Is this static function called anywhere in this patch?
>
> Unused static functions trigger a -Wunused-function compiler warning, which
> can break builds when CONFIG_WERROR is enabled.
Would just be unnecessary diff noise.
>
> [ ... ]
>
> > @@ -1683,7 +1698,8 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
> > if (err)
> > return err;
> > }
> > - if (pop_log)
> > + /* Preserve warning output across branch explorations. */
> > + if (pop_log && !(env->warnings && bpf_verifier_warn_needed(&env->log)))
> > bpf_vlog_reset(&env->log, head->log_pos);
> > if (insn_idx)
> > *insn_idx = head->insn_idx;
>
> Can this completely disable log resets once a warning is emitted?
>
> If a user requests both standard logging and warning logging, both verbose()
> and warn() will actively write to the log buffer.
>
> If log pruning is permanently disabled after the first warning, will the
> verifier dump the state of every successfully verified instruction into the
> log for all subsequent branch explorations?
>
> Because the log buffer acts as a rotating buffer by default, could this
> flood of output wrap around and overwrite the warning that was meant to be
> preserved?
>
> Would it be better to track the log position of the warning and reset to
> the maximum of head->log_pos and the warning position?
Good point, should probably do this only when log_level == 16.
>
> [ ... ]
>
> > @@ -18803,7 +18819,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
> >
> > ret = do_check(env);
> > out:
> > - if (!ret && pop_log)
> > + if (!ret && pop_log &&
> > + !(env->warnings && bpf_verifier_warn_needed(&env->log)))
> > bpf_vlog_reset(&env->log, 0);
> > free_states(env);
> > return ret;
>
> Could the same log buffer wrapping issue happen here when log resets are
> disabled after a warning?
Likewise, will fix.
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=1
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages
2026-04-18 17:16 ` [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages Kumar Kartikeya Dwivedi
2026-04-18 17:42 ` sashiko-bot
@ 2026-04-20 13:37 ` Mykyta Yatsenko
2026-04-20 15:26 ` Kumar Kartikeya Dwivedi
1 sibling, 1 reply; 22+ messages in thread
From: Mykyta Yatsenko @ 2026-04-20 13:37 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
On 4/18/26 6:16 PM, Kumar Kartikeya Dwivedi wrote:
> Add a mode where log_level 16 can be used to receive warnings and have
> an associated log buffer. Add a warn() macro that emits messages to log
> buffer without any restrictions, aggregate the warnings emitted, and
> then use it to decide whether we reset the log or not.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
> Documentation/bpf/kfuncs.rst | 4 +++-
> include/linux/bpf_verifier.h | 11 +++++++++--
> kernel/bpf/log.c | 2 +-
> kernel/bpf/verifier.c | 21 +++++++++++++++++++--
> 4 files changed, 32 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
> index 75e6c078e0e7..47d60011d8eb 100644
> --- a/Documentation/bpf/kfuncs.rst
> +++ b/Documentation/bpf/kfuncs.rst
> @@ -353,7 +353,9 @@ marked with KF_DEPRECATED should also have any relevant information
> captured in its kernel doc. Such information typically includes the
> kfunc's expected remaining lifespan, a recommendation for new
> functionality that can replace it if any is available, and possibly a
> -rationale for why it is being removed.
> +rationale for why it is being removed. When verifier warning logging is
> +requested, the verifier will emit a warning when a BPF program uses a
> +deprecated kfunc.
>
> Note that while on some occasions, a KF_DEPRECATED kfunc may continue to be
> supported and have its KF_DEPRECATED flag removed, it is likely to be far more
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index b148f816f25b..515afcb83ec7 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -689,15 +689,21 @@ struct bpf_verifier_log {
> #define BPF_LOG_LEVEL2 2
> #define BPF_LOG_STATS 4
> #define BPF_LOG_FIXED 8
> +#define BPF_LOG_LEVEL_WARN 16
> #define BPF_LOG_LEVEL (BPF_LOG_LEVEL1 | BPF_LOG_LEVEL2)
> -#define BPF_LOG_MASK (BPF_LOG_LEVEL | BPF_LOG_STATS | BPF_LOG_FIXED)
> +#define BPF_LOG_MASK (BPF_LOG_LEVEL | BPF_LOG_STATS | BPF_LOG_FIXED | BPF_LOG_LEVEL_WARN)
> #define BPF_LOG_KERNEL (BPF_LOG_MASK + 1) /* kernel internal flag */
> #define BPF_LOG_MIN_ALIGNMENT 8U
> #define BPF_LOG_ALIGNMENT 40U
>
> static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
> {
> - return log && log->level;
> + return log && (log->level & ~BPF_LOG_LEVEL_WARN);
> +}
> +
> +static inline bool bpf_verifier_warn_needed(const struct bpf_verifier_log *log)
> +{
> + return log && (log->level & BPF_LOG_LEVEL_WARN);
> }
>
It looks like there is a little inconsistency in how we handle warnings
vs stats and other log type, all other log types go through generic
verbose function and have level check inlined in verifier, while
warnings have a separate warn() func.
I think warning should either follow the existing design similar to
other types or we should consistently refactor all log types.
> #define BPF_MAX_SUBPROGS 256
> @@ -848,6 +854,7 @@ struct bpf_verifier_env {
> bool bypass_spec_v4;
> bool seen_direct_write;
> bool seen_exception;
> + bool warnings;
nit: bool has_warnings is a bit more verbose, but I think better
describes this variable.
> struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
> const struct bpf_line_info *prev_linfo;
> struct bpf_verifier_log log;
> diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
> index 011e4ec25acd..2cf79fac8d43 100644
> --- a/kernel/bpf/log.c
> +++ b/kernel/bpf/log.c
> @@ -154,7 +154,7 @@ void bpf_vlog_reset(struct bpf_verifier_log *log, u64 new_pos)
> if (WARN_ON_ONCE(new_pos > log->end_pos))
> return;
>
> - if (!bpf_verifier_log_needed(log) || log->level == BPF_LOG_KERNEL)
> + if (!log || log->level == 0 || log->level == BPF_LOG_KERNEL)
> return;
>
> /* if position to which we reset is beyond current log window,
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 69d75515ed3f..8de2a4e5f5de 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -47,6 +47,7 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
> enum bpf_features {
> BPF_FEAT_RDONLY_CAST_TO_VOID = 0,
> BPF_FEAT_STREAMS = 1,
> + BPF_FEAT_VERIFIER_WARNINGS = 2,
What is the usage scenario for feature detection for verifier warnings?
My gut feeling is that if you don't have warnings feature, you don't
have warnings, and bitmask should just work if you pass warnings bit
it's noop.
> __MAX_BPF_FEAT,
> };
>
> @@ -282,6 +283,20 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
> va_end(args);
> }
>
> +__printf(2, 3) static void warn(void *private_data, const char *fmt, ...)
> +{
> + struct bpf_verifier_env *env = private_data;
> + va_list args;
> +
> + if (!bpf_verifier_warn_needed(&env->log))
> + return;
> +
> + va_start(args, fmt);
> + bpf_verifier_vlog(&env->log, fmt, args);
> + va_end(args);
> + env->warnings = true;
> +}
> +
> static void verbose_invalid_scalar(struct bpf_verifier_env *env,
> struct bpf_reg_state *reg,
> struct bpf_retval_range range, const char *ctx,
> @@ -1683,7 +1698,8 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
> if (err)
> return err;
> }
> - if (pop_log)
> + /* Preserve warning output across branch explorations. */
> + if (pop_log && !(env->warnings && bpf_verifier_warn_needed(&env->log)))
Is it possible that env->warnings == true, but
bpf_verifier_warn_needed() == false? As far as I see you just check if
`!env->warnings` which better correspond to the comment.
> bpf_vlog_reset(&env->log, head->log_pos);
> if (insn_idx)
> *insn_idx = head->insn_idx;
> @@ -18803,7 +18819,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
>
> ret = do_check(env);
> out:
> - if (!ret && pop_log)
> + if (!ret && pop_log &&
> + !(env->warnings && bpf_verifier_warn_needed(&env->log)))
> bpf_vlog_reset(&env->log, 0);
> free_states(env);
> return ret;
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages
2026-04-20 13:37 ` Mykyta Yatsenko
@ 2026-04-20 15:26 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-20 15:26 UTC (permalink / raw)
To: Mykyta Yatsenko
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
On Mon, 20 Apr 2026 at 15:37, Mykyta Yatsenko
<mykyta.yatsenko5@gmail.com> wrote:
>
>
>
> On 4/18/26 6:16 PM, Kumar Kartikeya Dwivedi wrote:
> > Add a mode where log_level 16 can be used to receive warnings and have
> > an associated log buffer. Add a warn() macro that emits messages to log
> > buffer without any restrictions, aggregate the warnings emitted, and
> > then use it to decide whether we reset the log or not.
> >
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > ---
> > Documentation/bpf/kfuncs.rst | 4 +++-
> > include/linux/bpf_verifier.h | 11 +++++++++--
> > kernel/bpf/log.c | 2 +-
> > kernel/bpf/verifier.c | 21 +++++++++++++++++++--
> > 4 files changed, 32 insertions(+), 6 deletions(-)
> >
> > diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
> > index 75e6c078e0e7..47d60011d8eb 100644
> > --- a/Documentation/bpf/kfuncs.rst
> > +++ b/Documentation/bpf/kfuncs.rst
> > @@ -353,7 +353,9 @@ marked with KF_DEPRECATED should also have any relevant information
> > captured in its kernel doc. Such information typically includes the
> > kfunc's expected remaining lifespan, a recommendation for new
> > functionality that can replace it if any is available, and possibly a
> > -rationale for why it is being removed.
> > +rationale for why it is being removed. When verifier warning logging is
> > +requested, the verifier will emit a warning when a BPF program uses a
> > +deprecated kfunc.
> >
> > Note that while on some occasions, a KF_DEPRECATED kfunc may continue to be
> > supported and have its KF_DEPRECATED flag removed, it is likely to be far more
> > diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> > index b148f816f25b..515afcb83ec7 100644
> > --- a/include/linux/bpf_verifier.h
> > +++ b/include/linux/bpf_verifier.h
> > @@ -689,15 +689,21 @@ struct bpf_verifier_log {
> > #define BPF_LOG_LEVEL2 2
> > #define BPF_LOG_STATS 4
> > #define BPF_LOG_FIXED 8
> > +#define BPF_LOG_LEVEL_WARN 16
> > #define BPF_LOG_LEVEL (BPF_LOG_LEVEL1 | BPF_LOG_LEVEL2)
> > -#define BPF_LOG_MASK (BPF_LOG_LEVEL | BPF_LOG_STATS | BPF_LOG_FIXED)
> > +#define BPF_LOG_MASK (BPF_LOG_LEVEL | BPF_LOG_STATS | BPF_LOG_FIXED | BPF_LOG_LEVEL_WARN)
> > #define BPF_LOG_KERNEL (BPF_LOG_MASK + 1) /* kernel internal flag */
> > #define BPF_LOG_MIN_ALIGNMENT 8U
> > #define BPF_LOG_ALIGNMENT 40U
> >
> > static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
> > {
> > - return log && log->level;
> > + return log && (log->level & ~BPF_LOG_LEVEL_WARN);
> > +}
> > +
> > +static inline bool bpf_verifier_warn_needed(const struct bpf_verifier_log *log)
> > +{
> > + return log && (log->level & BPF_LOG_LEVEL_WARN);
> > }
> >
>
> It looks like there is a little inconsistency in how we handle warnings
> vs stats and other log type, all other log types go through generic
> verbose function and have level check inlined in verifier, while
> warnings have a separate warn() func.
> I think warning should either follow the existing design similar to
> other types or we should consistently refactor all log types.
I think the predicate for both is going to be different, in the end
both call the same functions.
Do you have an example of what you have in mind?
>
> > #define BPF_MAX_SUBPROGS 256
> > @@ -848,6 +854,7 @@ struct bpf_verifier_env {
> > bool bypass_spec_v4;
> > bool seen_direct_write;
> > bool seen_exception;
> > + bool warnings;
>
> nit: bool has_warnings is a bit more verbose, but I think better
> describes this variable.
Ack, will change.
>
> > struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
> > const struct bpf_line_info *prev_linfo;
> > struct bpf_verifier_log log;
> > diff --git a/kernel/bpf/log.c b/kernel/bpf/log.c
> > index 011e4ec25acd..2cf79fac8d43 100644
> > --- a/kernel/bpf/log.c
> > +++ b/kernel/bpf/log.c
> > @@ -154,7 +154,7 @@ void bpf_vlog_reset(struct bpf_verifier_log *log, u64 new_pos)
> > if (WARN_ON_ONCE(new_pos > log->end_pos))
> > return;
> >
> > - if (!bpf_verifier_log_needed(log) || log->level == BPF_LOG_KERNEL)
> > + if (!log || log->level == 0 || log->level == BPF_LOG_KERNEL)
> > return;
> >
> > /* if position to which we reset is beyond current log window,
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 69d75515ed3f..8de2a4e5f5de 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -47,6 +47,7 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
> > enum bpf_features {
> > BPF_FEAT_RDONLY_CAST_TO_VOID = 0,
> > BPF_FEAT_STREAMS = 1,
> > + BPF_FEAT_VERIFIER_WARNINGS = 2,
>
> What is the usage scenario for feature detection for verifier warnings?
> My gut feeling is that if you don't have warnings feature, you don't
> have warnings, and bitmask should just work if you pass warnings bit
> it's noop.
I will check, if it isn't needed for backwards compat with new libbpf
on older kernel I will drop it.
>
> > __MAX_BPF_FEAT,
> > };
> >
> > @@ -282,6 +283,20 @@ __printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)
> > va_end(args);
> > }
> >
> > +__printf(2, 3) static void warn(void *private_data, const char *fmt, ...)
> > +{
> > + struct bpf_verifier_env *env = private_data;
> > + va_list args;
> > +
> > + if (!bpf_verifier_warn_needed(&env->log))
> > + return;
> > +
> > + va_start(args, fmt);
> > + bpf_verifier_vlog(&env->log, fmt, args);
> > + va_end(args);
> > + env->warnings = true;
> > +}
> > +
> > static void verbose_invalid_scalar(struct bpf_verifier_env *env,
> > struct bpf_reg_state *reg,
> > struct bpf_retval_range range, const char *ctx,
> > @@ -1683,7 +1698,8 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
> > if (err)
> > return err;
> > }
> > - if (pop_log)
> > + /* Preserve warning output across branch explorations. */
> > + if (pop_log && !(env->warnings && bpf_verifier_warn_needed(&env->log)))
>
> Is it possible that env->warnings == true, but
> bpf_verifier_warn_needed() == false? As far as I see you just check if
> `!env->warnings` which better correspond to the comment.
Will check.
>
> > bpf_vlog_reset(&env->log, head->log_pos);
> > if (insn_idx)
> > *insn_idx = head->insn_idx;
> > @@ -18803,7 +18819,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
> >
> > ret = do_check(env);
> > out:
> > - if (!ret && pop_log)
> > + if (!ret && pop_log &&
> > + !(env->warnings && bpf_verifier_warn_needed(&env->log)))
> > bpf_vlog_reset(&env->log, 0);
> > free_states(env);
> > return ret;
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation
2026-04-18 17:16 [PATCH bpf-next v3 0/4] Add support to emit verifier warnings Kumar Kartikeya Dwivedi
2026-04-18 17:16 ` [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages Kumar Kartikeya Dwivedi
@ 2026-04-18 17:16 ` Kumar Kartikeya Dwivedi
2026-04-18 18:06 ` sashiko-bot
` (2 more replies)
2026-04-18 17:16 ` [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads Kumar Kartikeya Dwivedi
2026-04-18 17:16 ` [PATCH bpf-next v3 4/4] selftests/bpf: Test verifier warning logging Kumar Kartikeya Dwivedi
3 siblings, 3 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 17:16 UTC (permalink / raw)
To: bpf
Cc: Vineet Gupta, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Eduard Zingerman,
Ihor Solodrai, kkd, kernel-team
Documentation for KF_DEPRECATED documents the expectations and
recommendations for deprecating existing kfuncs. Until now, such a flag
never materialized. Unlike other kfunc flags, more context needs to be
carried with such a flag, thus we introduce __bpf_kfunc_mark_deprecated
as a replacement for the (non-existent) kfunc flag which takes a
replacement kfunc to provide recommendations to the user.
Apply it to all existing impl-suffixed kfuncs that are preserved for
backwards compatibility for a few kernel releases, after we introduced
versions with KF_IMPLICIT_ARGS.
Note that this is only supported by LLVM-compiled kernels as of writing,
since GCC does not support for BTF declaration tags.
Cc: Vineet Gupta <vineet.gupta@linux.dev>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
Documentation/bpf/kfuncs.rst | 42 ++++++++++---------
include/linux/bpf_verifier.h | 1 +
include/linux/btf.h | 5 +++
include/linux/compiler_types.h | 14 ++++++-
kernel/bpf/helpers.c | 8 ++++
kernel/bpf/verifier.c | 41 ++++++++++++++++++
.../selftests/bpf/test_kmods/bpf_testmod.c | 1 +
7 files changed, 90 insertions(+), 22 deletions(-)
diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 47d60011d8eb..5beef4432a8d 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -344,22 +344,23 @@ RCU protection but do not take RCU protected arguments.
.. _KF_deprecated_flag:
-2.5.8 KF_DEPRECATED flag
-------------------------
-
-The KF_DEPRECATED flag is used for kfuncs which are scheduled to be
-changed or removed in a subsequent kernel release. A kfunc that is
-marked with KF_DEPRECATED should also have any relevant information
-captured in its kernel doc. Such information typically includes the
-kfunc's expected remaining lifespan, a recommendation for new
-functionality that can replace it if any is available, and possibly a
-rationale for why it is being removed. When verifier warning logging is
-requested, the verifier will emit a warning when a BPF program uses a
-deprecated kfunc.
-
-Note that while on some occasions, a KF_DEPRECATED kfunc may continue to be
-supported and have its KF_DEPRECATED flag removed, it is likely to be far more
-difficult to remove a KF_DEPRECATED flag after it's been added than it is to
+2.5.8 Deprecated kfunc annotation
+---------------------------------
+
+The ``__bpf_kfunc_mark_deprecated(replacement)`` annotation is used for
+kfuncs which are scheduled to be changed or removed in a subsequent
+kernel release. A kfunc annotated with
+``__bpf_kfunc_mark_deprecated(replacement)`` should also have any
+relevant information captured in its kernel doc. Such information
+typically includes the kfunc's expected remaining lifespan, a
+recommendation for new functionality that can replace it if any is
+available, and possibly a rationale for why it is being removed. When
+verifier warning logging is requested, the verifier will emit a warning
+when a BPF program uses such a deprecated kfunc.
+
+Note that while on some occasions, a deprecated kfunc may continue to be
+supported and have its deprecation annotation removed, it is likely to be far
+more difficult to remove such an annotation after it's been added than it is to
prevent it from being added in the first place. As described in
:ref:`BPF_kfunc_lifecycle_expectations`, users that rely on specific kfuncs are
encouraged to make their use-cases known as early as possible, and participate
@@ -534,10 +535,11 @@ maintainer if it turns out that the kfunc is actually being used.
It's expected that the common case will be that kfuncs will go through a
deprecation period rather than being changed or removed without warning. As
-described in :ref:`KF_deprecated_flag`, the kfunc framework provides the
-KF_DEPRECATED flag to kfunc developers to signal to users that a kfunc has been
-deprecated. Once a kfunc has been marked with KF_DEPRECATED, the following
-procedure is followed for removal:
+described in :ref:`KF_deprecated_flag`, the kfunc framework provides
+the ``__bpf_kfunc_mark_deprecated(replacement)`` annotation to signal to users
+that a kfunc has been deprecated and to identify the replacement kfunc. Once a
+kfunc has been marked deprecated, the following procedure is followed for
+removal:
1. Any relevant information for deprecated kfuncs is documented in the kfunc's
kernel docs. This documentation will typically include the kfunc's expected
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 515afcb83ec7..b70081fe24d8 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1461,6 +1461,7 @@ struct bpf_kfunc_desc {
u32 func_id;
s32 imm;
u16 offset;
+ bool warned_deprecated;
unsigned long addr;
};
diff --git a/include/linux/btf.h b/include/linux/btf.h
index 48108471c5b1..c75ecef67096 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -88,6 +88,11 @@
*/
#define __bpf_kfunc __used __retain __noclone noinline
+#define BPF_KFUNC_DECL_TAG_DEPRECATED "bpf:kfunc:deprecated:"
+
+#define __bpf_kfunc_mark_deprecated(replacement) \
+ BTF_DECL_TAG(BPF_KFUNC_DECL_TAG_DEPRECATED __stringify(replacement))
+
#define __bpf_kfunc_start_defs() \
__diag_push(); \
__diag_ignore_all("-Wmissing-declarations", \
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index e8fd77593b68..ba5b20e30d4d 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -35,10 +35,20 @@
* see https://github.com/rust-lang/rust-bindgen/issues/2244.
*/
#if defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \
- __has_attribute(btf_type_tag) && !defined(__BINDGEN__)
-# define BTF_TYPE_TAG(value) __attribute__((btf_type_tag(#value)))
+ !defined(__BINDGEN__)
+# if __has_attribute(btf_type_tag)
+# define BTF_TYPE_TAG(value) __attribute__((btf_type_tag(#value)))
+# else
+# define BTF_TYPE_TAG(value) /* nothing */
+# endif
+# if __has_attribute(btf_decl_tag)
+# define BTF_DECL_TAG(value) __attribute__((btf_decl_tag(value)))
+# else
+# define BTF_DECL_TAG(value) /* nothing */
+# endif
#else
# define BTF_TYPE_TAG(value) /* nothing */
+# define BTF_DECL_TAG(value) /* nothing */
#endif
#include <linux/compiler-context-analysis.h>
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 2bb60200c266..69b4eae257a1 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -2343,6 +2343,7 @@ __bpf_kfunc void *bpf_obj_new(u64 local_type_id__k, struct btf_struct_meta *meta
return p;
}
+__bpf_kfunc_mark_deprecated(bpf_obj_new)
__bpf_kfunc void *bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign)
{
return bpf_obj_new(local_type_id__k, meta__ign);
@@ -2367,6 +2368,7 @@ __bpf_kfunc void *bpf_percpu_obj_new(u64 local_type_id__k, struct btf_struct_met
return bpf_mem_alloc(&bpf_global_percpu_ma, size);
}
+__bpf_kfunc_mark_deprecated(bpf_percpu_obj_new)
__bpf_kfunc void *bpf_percpu_obj_new_impl(u64 local_type_id__k, void *meta__ign)
{
return bpf_percpu_obj_new(local_type_id__k, meta__ign);
@@ -2410,6 +2412,7 @@ __bpf_kfunc void bpf_obj_drop(void *p__alloc, struct btf_struct_meta *meta)
__bpf_obj_drop_impl(p, meta ? meta->record : NULL, false);
}
+__bpf_kfunc_mark_deprecated(bpf_obj_drop)
__bpf_kfunc void bpf_obj_drop_impl(void *p__alloc, void *meta__ign)
{
return bpf_obj_drop(p__alloc, meta__ign);
@@ -2428,6 +2431,7 @@ __bpf_kfunc void bpf_percpu_obj_drop(void *p__alloc, struct btf_struct_meta *met
bpf_mem_free_rcu(&bpf_global_percpu_ma, p__alloc);
}
+__bpf_kfunc_mark_deprecated(bpf_percpu_obj_drop)
__bpf_kfunc void bpf_percpu_obj_drop_impl(void *p__alloc, void *meta__ign)
{
bpf_percpu_obj_drop(p__alloc, meta__ign);
@@ -2460,6 +2464,7 @@ __bpf_kfunc void *bpf_refcount_acquire(void *p__refcounted_kptr, struct btf_stru
return (void *)p__refcounted_kptr;
}
+__bpf_kfunc_mark_deprecated(bpf_refcount_acquire)
__bpf_kfunc void *bpf_refcount_acquire_impl(void *p__refcounted_kptr, void *meta__ign)
{
return bpf_refcount_acquire(p__refcounted_kptr, meta__ign);
@@ -2514,6 +2519,7 @@ __bpf_kfunc int bpf_list_push_front(struct bpf_list_head *head,
return __bpf_list_add(n, head, false, meta ? meta->record : NULL, off);
}
+__bpf_kfunc_mark_deprecated(bpf_list_push_front)
__bpf_kfunc int bpf_list_push_front_impl(struct bpf_list_head *head,
struct bpf_list_node *node,
void *meta__ign, u64 off)
@@ -2543,6 +2549,7 @@ __bpf_kfunc int bpf_list_push_back(struct bpf_list_head *head,
return __bpf_list_add(n, head, true, meta ? meta->record : NULL, off);
}
+__bpf_kfunc_mark_deprecated(bpf_list_push_back)
__bpf_kfunc int bpf_list_push_back_impl(struct bpf_list_head *head,
struct bpf_list_node *node,
void *meta__ign, u64 off)
@@ -2683,6 +2690,7 @@ __bpf_kfunc int bpf_rbtree_add(struct bpf_rb_root *root,
return __bpf_rbtree_add(root, n, (void *)less, meta ? meta->record : NULL, off);
}
+__bpf_kfunc_mark_deprecated(bpf_rbtree_add)
__bpf_kfunc int bpf_rbtree_add_impl(struct bpf_rb_root *root, struct bpf_rb_node *node,
bool (less)(struct bpf_rb_node *a, const struct bpf_rb_node *b),
void *meta__ign, u64 off)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8de2a4e5f5de..1f3b44f47949 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12987,6 +12987,45 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
static int process_bpf_exit_full(struct bpf_verifier_env *env,
bool *do_print_state, bool exception_exit);
+static void warn_for_deprecated_kfuncs(struct bpf_verifier_env *env,
+ struct bpf_kfunc_call_arg_meta *meta,
+ int insn_idx, s16 offset)
+{
+ const struct bpf_line_info *linfo;
+ const char *replacement;
+ const struct btf_type *t;
+ struct bpf_kfunc_desc *desc;
+ const char *file;
+ int line_num;
+
+ if (!env->prog->aux->btf)
+ return;
+
+ t = btf_type_by_id(meta->btf, meta->func_id);
+ replacement = btf_find_decl_tag_value(meta->btf, t, -1, BPF_KFUNC_DECL_TAG_DEPRECATED);
+ if (IS_ERR(replacement) || str_is_empty(replacement))
+ return;
+
+ desc = find_kfunc_desc(env->prog, meta->func_id, offset);
+ if (!desc || desc->warned_deprecated)
+ return;
+
+ linfo = bpf_find_linfo(env->prog, insn_idx);
+ if (linfo) {
+ bpf_get_linfo_file_line(env->prog->aux->btf, linfo, &file, NULL, &line_num);
+ warn(env, "%s:%d (insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
+ file, line_num, insn_idx, meta->func_name);
+ } else {
+ warn(env, "(insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
+ insn_idx, meta->func_name);
+ }
+
+ warn(env, "Switch to kfunc %s() instead.\n", replacement);
+ warn(env, "For older kernels, choose the correct kfunc using bpf_ksym_exists().\n");
+
+ desc->warned_deprecated = true;
+}
+
static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
int *insn_idx_p)
{
@@ -13016,6 +13055,8 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
insn_aux->is_iter_next = bpf_is_iter_next_kfunc(&meta);
+ warn_for_deprecated_kfuncs(env, &meta, insn_idx, insn->off);
+
if (!insn->off &&
(insn->imm == special_kfunc_list[KF_bpf_res_spin_lock] ||
insn->imm == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])) {
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index d876314a4d67..5a57a92a8f8c 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1252,6 +1252,7 @@ __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, st
__bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
__bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
+__bpf_kfunc_mark_deprecated(bpf_kfunc_implicit_arg_legacy)
__bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux);
/* hook targets */
--
2.52.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation
2026-04-18 17:16 ` [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation Kumar Kartikeya Dwivedi
@ 2026-04-18 18:06 ` sashiko-bot
2026-04-18 20:34 ` Kumar Kartikeya Dwivedi
2026-04-20 14:21 ` Mykyta Yatsenko
2026-04-20 18:15 ` David Faust
2 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-04-18 18:06 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi; +Cc: bpf
Sashiko AI review found 1 potential issue(s):
- [Medium] Programs loaded without BTF will silently bypass deprecation warnings.
--
commit 90e501a1948d112dbb3b96a16249f1f76f84b290
Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
bpf: Introduce __bpf_kfunc_mark_deprecated annotation
This commit introduces the __bpf_kfunc_mark_deprecated macro to annotate
deprecated kfuncs with a replacement. It applies this annotation to existing
impl-suffixed kfuncs and modifies the verifier to emit warnings when these
deprecated kfuncs are used.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -12987,6 +12987,45 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
> static int process_bpf_exit_full(struct bpf_verifier_env *env,
> bool *do_print_state, bool exception_exit);
>
> +static void warn_for_deprecated_kfuncs(struct bpf_verifier_env *env,
> + struct bpf_kfunc_call_arg_meta *meta,
> + int insn_idx, s16 offset)
> +{
> + const struct bpf_line_info *linfo;
> + const char *replacement;
> + const struct btf_type *t;
> + struct bpf_kfunc_desc *desc;
> + const char *file;
> + int line_num;
> +
> + if (!env->prog->aux->btf)
> + return;
Will programs loaded without BTF silently bypass the deprecation warning?
The kfunc deprecation tags are stored in the kernel or module BTF
(meta->btf), not the program's BTF.
If we return early here, we skip evaluating the tag entirely. There is a
fallback branch below that handles missing line info which could be used to
emit the warning even if the program doesn't have BTF.
> +
> + t = btf_type_by_id(meta->btf, meta->func_id);
> + replacement = btf_find_decl_tag_value(meta->btf, t, -1, BPF_KFUNC_DECL_TAG_DEPRECATED);
[ ... ]
> + linfo = bpf_find_linfo(env->prog, insn_idx);
> + if (linfo) {
> + bpf_get_linfo_file_line(env->prog->aux->btf, linfo, &file, NULL, &line_num);
> + warn(env, "%s:%d (insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> + file, line_num, insn_idx, meta->func_name);
> + } else {
> + warn(env, "(insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> + insn_idx, meta->func_name);
> + }
> +
> + warn(env, "Switch to kfunc %s() instead.\n", replacement);
> + warn(env, "For older kernels, choose the correct kfunc using bpf_ksym_exists().\n");
> +
> + desc->warned_deprecated = true;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=2
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation
2026-04-18 18:06 ` sashiko-bot
@ 2026-04-18 20:34 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 20:34 UTC (permalink / raw)
To: sashiko; +Cc: bpf
On Sat, 18 Apr 2026 at 19:06, <sashiko-bot@kernel.org> wrote:
>
> Sashiko AI review found 1 potential issue(s):
> - [Medium] Programs loaded without BTF will silently bypass deprecation warnings.
> --
>
> commit 90e501a1948d112dbb3b96a16249f1f76f84b290
> Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>
> bpf: Introduce __bpf_kfunc_mark_deprecated annotation
>
> This commit introduces the __bpf_kfunc_mark_deprecated macro to annotate
> deprecated kfuncs with a replacement. It applies this annotation to existing
> impl-suffixed kfuncs and modifies the verifier to emit warnings when these
> deprecated kfuncs are used.
>
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
>
> [ ... ]
>
> > @@ -12987,6 +12987,45 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
> > static int process_bpf_exit_full(struct bpf_verifier_env *env,
> > bool *do_print_state, bool exception_exit);
> >
> > +static void warn_for_deprecated_kfuncs(struct bpf_verifier_env *env,
> > + struct bpf_kfunc_call_arg_meta *meta,
> > + int insn_idx, s16 offset)
> > +{
> > + const struct bpf_line_info *linfo;
> > + const char *replacement;
> > + const struct btf_type *t;
> > + struct bpf_kfunc_desc *desc;
> > + const char *file;
> > + int line_num;
> > +
> > + if (!env->prog->aux->btf)
> > + return;
>
> Will programs loaded without BTF silently bypass the deprecation warning?
>
> The kfunc deprecation tags are stored in the kernel or module BTF
> (meta->btf), not the program's BTF.
>
> If we return early here, we skip evaluating the tag entirely. There is a
> fallback branch below that handles missing line info which could be used to
> emit the warning even if the program doesn't have BTF.
Programs not using BTF are legacy, it is better to just ignore them.
Program BTF is needed for bpf_get_linfo_file_line().
>
> > +
> > + t = btf_type_by_id(meta->btf, meta->func_id);
> > + replacement = btf_find_decl_tag_value(meta->btf, t, -1, BPF_KFUNC_DECL_TAG_DEPRECATED);
>
> [ ... ]
>
> > + linfo = bpf_find_linfo(env->prog, insn_idx);
> > + if (linfo) {
> > + bpf_get_linfo_file_line(env->prog->aux->btf, linfo, &file, NULL, &line_num);
> > + warn(env, "%s:%d (insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> > + file, line_num, insn_idx, meta->func_name);
> > + } else {
> > + warn(env, "(insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> > + insn_idx, meta->func_name);
> > + }
> > +
> > + warn(env, "Switch to kfunc %s() instead.\n", replacement);
> > + warn(env, "For older kernels, choose the correct kfunc using bpf_ksym_exists().\n");
> > +
> > + desc->warned_deprecated = true;
> > +}
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=2
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation
2026-04-18 17:16 ` [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation Kumar Kartikeya Dwivedi
2026-04-18 18:06 ` sashiko-bot
@ 2026-04-20 14:21 ` Mykyta Yatsenko
2026-04-20 15:27 ` Kumar Kartikeya Dwivedi
2026-04-20 18:15 ` David Faust
2 siblings, 1 reply; 22+ messages in thread
From: Mykyta Yatsenko @ 2026-04-20 14:21 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Vineet Gupta, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Eduard Zingerman,
Ihor Solodrai, kkd, kernel-team
On 4/18/26 6:16 PM, Kumar Kartikeya Dwivedi wrote:
> Documentation for KF_DEPRECATED documents the expectations and
> recommendations for deprecating existing kfuncs. Until now, such a flag
> never materialized. Unlike other kfunc flags, more context needs to be
> carried with such a flag, thus we introduce __bpf_kfunc_mark_deprecated
> as a replacement for the (non-existent) kfunc flag which takes a
> replacement kfunc to provide recommendations to the user.
>
> Apply it to all existing impl-suffixed kfuncs that are preserved for
> backwards compatibility for a few kernel releases, after we introduced
> versions with KF_IMPLICIT_ARGS.
>
> Note that this is only supported by LLVM-compiled kernels as of writing,
> since GCC does not support for BTF declaration tags.
>
> Cc: Vineet Gupta <vineet.gupta@linux.dev>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
> Documentation/bpf/kfuncs.rst | 42 ++++++++++---------
> include/linux/bpf_verifier.h | 1 +
> include/linux/btf.h | 5 +++
> include/linux/compiler_types.h | 14 ++++++-
> kernel/bpf/helpers.c | 8 ++++
> kernel/bpf/verifier.c | 41 ++++++++++++++++++
> .../selftests/bpf/test_kmods/bpf_testmod.c | 1 +
> 7 files changed, 90 insertions(+), 22 deletions(-)
>
...
> +static void warn_for_deprecated_kfuncs(struct bpf_verifier_env *env,
> + struct bpf_kfunc_call_arg_meta *meta,
> + int insn_idx, s16 offset)
> +{
> + const struct bpf_line_info *linfo;
> + const char *replacement;
> + const struct btf_type *t;
> + struct bpf_kfunc_desc *desc;
> + const char *file;
> + int line_num;
> +
> + if (!env->prog->aux->btf)
> + return;
> +
> + t = btf_type_by_id(meta->btf, meta->func_id);
> + replacement = btf_find_decl_tag_value(meta->btf, t, -1, BPF_KFUNC_DECL_TAG_DEPRECATED);
> + if (IS_ERR(replacement) || str_is_empty(replacement))
> + return;
> +
> + desc = find_kfunc_desc(env->prog, meta->func_id, offset);
> + if (!desc || desc->warned_deprecated)
> + return;
> +
> + linfo = bpf_find_linfo(env->prog, insn_idx);
> + if (linfo) {
> + bpf_get_linfo_file_line(env->prog->aux->btf, linfo, &file, NULL, &line_num);
> + warn(env, "%s:%d (insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> + file, line_num, insn_idx, meta->func_name);
> + } else {
> + warn(env, "(insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> + insn_idx, meta->func_name);
> + }
> +
> + warn(env, "Switch to kfunc %s() instead.\n", replacement);
> + warn(env, "For older kernels, choose the correct kfunc using bpf_ksym_exists().\n");
Is it right to reference bpf_ksym_exists() macro from the verifier log,
what if user does not use bpf_helpers.h header?
> +
> + desc->warned_deprecated = true;
> +}
> +
> static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> int *insn_idx_p)
> {
> @@ -13016,6 +13055,8 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>
> insn_aux->is_iter_next = bpf_is_iter_next_kfunc(&meta);
>
> + warn_for_deprecated_kfuncs(env, &meta, insn_idx, insn->off);
> +
> if (!insn->off &&
> (insn->imm == special_kfunc_list[KF_bpf_res_spin_lock] ||
> insn->imm == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])) {
> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> index d876314a4d67..5a57a92a8f8c 100644
> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> @@ -1252,6 +1252,7 @@ __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, st
>
> __bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
> __bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
> +__bpf_kfunc_mark_deprecated(bpf_kfunc_implicit_arg_legacy)
> __bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux);
>
> /* hook targets */
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation
2026-04-20 14:21 ` Mykyta Yatsenko
@ 2026-04-20 15:27 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-20 15:27 UTC (permalink / raw)
To: Mykyta Yatsenko
Cc: bpf, Vineet Gupta, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Eduard Zingerman,
Ihor Solodrai, kkd, kernel-team
On Mon, 20 Apr 2026 at 16:21, Mykyta Yatsenko
<mykyta.yatsenko5@gmail.com> wrote:
>
>
>
> On 4/18/26 6:16 PM, Kumar Kartikeya Dwivedi wrote:
> > Documentation for KF_DEPRECATED documents the expectations and
> > recommendations for deprecating existing kfuncs. Until now, such a flag
> > never materialized. Unlike other kfunc flags, more context needs to be
> > carried with such a flag, thus we introduce __bpf_kfunc_mark_deprecated
> > as a replacement for the (non-existent) kfunc flag which takes a
> > replacement kfunc to provide recommendations to the user.
> >
> > Apply it to all existing impl-suffixed kfuncs that are preserved for
> > backwards compatibility for a few kernel releases, after we introduced
> > versions with KF_IMPLICIT_ARGS.
> >
> > Note that this is only supported by LLVM-compiled kernels as of writing,
> > since GCC does not support for BTF declaration tags.
> >
> > Cc: Vineet Gupta <vineet.gupta@linux.dev>
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > ---
> > Documentation/bpf/kfuncs.rst | 42 ++++++++++---------
> > include/linux/bpf_verifier.h | 1 +
> > include/linux/btf.h | 5 +++
> > include/linux/compiler_types.h | 14 ++++++-
> > kernel/bpf/helpers.c | 8 ++++
> > kernel/bpf/verifier.c | 41 ++++++++++++++++++
> > .../selftests/bpf/test_kmods/bpf_testmod.c | 1 +
> > 7 files changed, 90 insertions(+), 22 deletions(-)
> >
> ...
> > +static void warn_for_deprecated_kfuncs(struct bpf_verifier_env *env,
> > + struct bpf_kfunc_call_arg_meta *meta,
> > + int insn_idx, s16 offset)
> > +{
> > + const struct bpf_line_info *linfo;
> > + const char *replacement;
> > + const struct btf_type *t;
> > + struct bpf_kfunc_desc *desc;
> > + const char *file;
> > + int line_num;
> > +
> > + if (!env->prog->aux->btf)
> > + return;
> > +
> > + t = btf_type_by_id(meta->btf, meta->func_id);
> > + replacement = btf_find_decl_tag_value(meta->btf, t, -1, BPF_KFUNC_DECL_TAG_DEPRECATED);
> > + if (IS_ERR(replacement) || str_is_empty(replacement))
> > + return;
> > +
> > + desc = find_kfunc_desc(env->prog, meta->func_id, offset);
> > + if (!desc || desc->warned_deprecated)
> > + return;
> > +
> > + linfo = bpf_find_linfo(env->prog, insn_idx);
> > + if (linfo) {
> > + bpf_get_linfo_file_line(env->prog->aux->btf, linfo, &file, NULL, &line_num);
> > + warn(env, "%s:%d (insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> > + file, line_num, insn_idx, meta->func_name);
> > + } else {
> > + warn(env, "(insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> > + insn_idx, meta->func_name);
> > + }
> > +
> > + warn(env, "Switch to kfunc %s() instead.\n", replacement);
> > + warn(env, "For older kernels, choose the correct kfunc using bpf_ksym_exists().\n");
>
> Is it right to reference bpf_ksym_exists() macro from the verifier log,
> what if user does not use bpf_helpers.h header?
>
I was hoping it's discoverable if people google around etc., the first
result (https://docs.ebpf.io/ebpf-library/libbpf/ebpf/bpf_ksym_exists)
also has a nice example.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation
2026-04-18 17:16 ` [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation Kumar Kartikeya Dwivedi
2026-04-18 18:06 ` sashiko-bot
2026-04-20 14:21 ` Mykyta Yatsenko
@ 2026-04-20 18:15 ` David Faust
2026-04-20 18:19 ` Kumar Kartikeya Dwivedi
2 siblings, 1 reply; 22+ messages in thread
From: David Faust @ 2026-04-20 18:15 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Vineet Gupta, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Eduard Zingerman,
Ihor Solodrai, kkd, kernel-team
On 4/18/26 10:16, Kumar Kartikeya Dwivedi wrote:
> Documentation for KF_DEPRECATED documents the expectations and
> recommendations for deprecating existing kfuncs. Until now, such a flag
> never materialized. Unlike other kfunc flags, more context needs to be
> carried with such a flag, thus we introduce __bpf_kfunc_mark_deprecated
> as a replacement for the (non-existent) kfunc flag which takes a
> replacement kfunc to provide recommendations to the user.
>
> Apply it to all existing impl-suffixed kfuncs that are preserved for
> backwards compatibility for a few kernel releases, after we introduced
> versions with KF_IMPLICIT_ARGS.
>
> Note that this is only supported by LLVM-compiled kernels as of writing,
> since GCC does not support for BTF declaration tags.
FYI support in GCC for BTF tags (both declaration and type) has been merged
and will be available in gcc 16:
https://inbox.sourceware.org/gcc-patches/f8ef5eca-4f1c-4efb-96a9-c65533e4573a@oracle.com/
We have been using it successfully with the selftest harness' __failure
__msg etc. macros with gcc master.
>
> Cc: Vineet Gupta <vineet.gupta@linux.dev>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
> Documentation/bpf/kfuncs.rst | 42 ++++++++++---------
> include/linux/bpf_verifier.h | 1 +
> include/linux/btf.h | 5 +++
> include/linux/compiler_types.h | 14 ++++++-
> kernel/bpf/helpers.c | 8 ++++
> kernel/bpf/verifier.c | 41 ++++++++++++++++++
> .../selftests/bpf/test_kmods/bpf_testmod.c | 1 +
> 7 files changed, 90 insertions(+), 22 deletions(-)
>
> diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
> index 47d60011d8eb..5beef4432a8d 100644
> --- a/Documentation/bpf/kfuncs.rst
> +++ b/Documentation/bpf/kfuncs.rst
> @@ -344,22 +344,23 @@ RCU protection but do not take RCU protected arguments.
>
> .. _KF_deprecated_flag:
>
> -2.5.8 KF_DEPRECATED flag
> -------------------------
> -
> -The KF_DEPRECATED flag is used for kfuncs which are scheduled to be
> -changed or removed in a subsequent kernel release. A kfunc that is
> -marked with KF_DEPRECATED should also have any relevant information
> -captured in its kernel doc. Such information typically includes the
> -kfunc's expected remaining lifespan, a recommendation for new
> -functionality that can replace it if any is available, and possibly a
> -rationale for why it is being removed. When verifier warning logging is
> -requested, the verifier will emit a warning when a BPF program uses a
> -deprecated kfunc.
> -
> -Note that while on some occasions, a KF_DEPRECATED kfunc may continue to be
> -supported and have its KF_DEPRECATED flag removed, it is likely to be far more
> -difficult to remove a KF_DEPRECATED flag after it's been added than it is to
> +2.5.8 Deprecated kfunc annotation
> +---------------------------------
> +
> +The ``__bpf_kfunc_mark_deprecated(replacement)`` annotation is used for
> +kfuncs which are scheduled to be changed or removed in a subsequent
> +kernel release. A kfunc annotated with
> +``__bpf_kfunc_mark_deprecated(replacement)`` should also have any
> +relevant information captured in its kernel doc. Such information
> +typically includes the kfunc's expected remaining lifespan, a
> +recommendation for new functionality that can replace it if any is
> +available, and possibly a rationale for why it is being removed. When
> +verifier warning logging is requested, the verifier will emit a warning
> +when a BPF program uses such a deprecated kfunc.
> +
> +Note that while on some occasions, a deprecated kfunc may continue to be
> +supported and have its deprecation annotation removed, it is likely to be far
> +more difficult to remove such an annotation after it's been added than it is to
> prevent it from being added in the first place. As described in
> :ref:`BPF_kfunc_lifecycle_expectations`, users that rely on specific kfuncs are
> encouraged to make their use-cases known as early as possible, and participate
> @@ -534,10 +535,11 @@ maintainer if it turns out that the kfunc is actually being used.
>
> It's expected that the common case will be that kfuncs will go through a
> deprecation period rather than being changed or removed without warning. As
> -described in :ref:`KF_deprecated_flag`, the kfunc framework provides the
> -KF_DEPRECATED flag to kfunc developers to signal to users that a kfunc has been
> -deprecated. Once a kfunc has been marked with KF_DEPRECATED, the following
> -procedure is followed for removal:
> +described in :ref:`KF_deprecated_flag`, the kfunc framework provides
> +the ``__bpf_kfunc_mark_deprecated(replacement)`` annotation to signal to users
> +that a kfunc has been deprecated and to identify the replacement kfunc. Once a
> +kfunc has been marked deprecated, the following procedure is followed for
> +removal:
>
> 1. Any relevant information for deprecated kfuncs is documented in the kfunc's
> kernel docs. This documentation will typically include the kfunc's expected
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index 515afcb83ec7..b70081fe24d8 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -1461,6 +1461,7 @@ struct bpf_kfunc_desc {
> u32 func_id;
> s32 imm;
> u16 offset;
> + bool warned_deprecated;
> unsigned long addr;
> };
>
> diff --git a/include/linux/btf.h b/include/linux/btf.h
> index 48108471c5b1..c75ecef67096 100644
> --- a/include/linux/btf.h
> +++ b/include/linux/btf.h
> @@ -88,6 +88,11 @@
> */
> #define __bpf_kfunc __used __retain __noclone noinline
>
> +#define BPF_KFUNC_DECL_TAG_DEPRECATED "bpf:kfunc:deprecated:"
> +
> +#define __bpf_kfunc_mark_deprecated(replacement) \
> + BTF_DECL_TAG(BPF_KFUNC_DECL_TAG_DEPRECATED __stringify(replacement))
> +
> #define __bpf_kfunc_start_defs() \
> __diag_push(); \
> __diag_ignore_all("-Wmissing-declarations", \
> diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
> index e8fd77593b68..ba5b20e30d4d 100644
> --- a/include/linux/compiler_types.h
> +++ b/include/linux/compiler_types.h
> @@ -35,10 +35,20 @@
> * see https://github.com/rust-lang/rust-bindgen/issues/2244.
> */
> #if defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \
> - __has_attribute(btf_type_tag) && !defined(__BINDGEN__)
> -# define BTF_TYPE_TAG(value) __attribute__((btf_type_tag(#value)))
> + !defined(__BINDGEN__)
> +# if __has_attribute(btf_type_tag)
> +# define BTF_TYPE_TAG(value) __attribute__((btf_type_tag(#value)))
> +# else
> +# define BTF_TYPE_TAG(value) /* nothing */
> +# endif
> +# if __has_attribute(btf_decl_tag)
> +# define BTF_DECL_TAG(value) __attribute__((btf_decl_tag(value)))
> +# else
> +# define BTF_DECL_TAG(value) /* nothing */
> +# endif
> #else
> # define BTF_TYPE_TAG(value) /* nothing */
> +# define BTF_DECL_TAG(value) /* nothing */
> #endif
>
> #include <linux/compiler-context-analysis.h>
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index 2bb60200c266..69b4eae257a1 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -2343,6 +2343,7 @@ __bpf_kfunc void *bpf_obj_new(u64 local_type_id__k, struct btf_struct_meta *meta
> return p;
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_obj_new)
> __bpf_kfunc void *bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign)
> {
> return bpf_obj_new(local_type_id__k, meta__ign);
> @@ -2367,6 +2368,7 @@ __bpf_kfunc void *bpf_percpu_obj_new(u64 local_type_id__k, struct btf_struct_met
> return bpf_mem_alloc(&bpf_global_percpu_ma, size);
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_percpu_obj_new)
> __bpf_kfunc void *bpf_percpu_obj_new_impl(u64 local_type_id__k, void *meta__ign)
> {
> return bpf_percpu_obj_new(local_type_id__k, meta__ign);
> @@ -2410,6 +2412,7 @@ __bpf_kfunc void bpf_obj_drop(void *p__alloc, struct btf_struct_meta *meta)
> __bpf_obj_drop_impl(p, meta ? meta->record : NULL, false);
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_obj_drop)
> __bpf_kfunc void bpf_obj_drop_impl(void *p__alloc, void *meta__ign)
> {
> return bpf_obj_drop(p__alloc, meta__ign);
> @@ -2428,6 +2431,7 @@ __bpf_kfunc void bpf_percpu_obj_drop(void *p__alloc, struct btf_struct_meta *met
> bpf_mem_free_rcu(&bpf_global_percpu_ma, p__alloc);
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_percpu_obj_drop)
> __bpf_kfunc void bpf_percpu_obj_drop_impl(void *p__alloc, void *meta__ign)
> {
> bpf_percpu_obj_drop(p__alloc, meta__ign);
> @@ -2460,6 +2464,7 @@ __bpf_kfunc void *bpf_refcount_acquire(void *p__refcounted_kptr, struct btf_stru
> return (void *)p__refcounted_kptr;
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_refcount_acquire)
> __bpf_kfunc void *bpf_refcount_acquire_impl(void *p__refcounted_kptr, void *meta__ign)
> {
> return bpf_refcount_acquire(p__refcounted_kptr, meta__ign);
> @@ -2514,6 +2519,7 @@ __bpf_kfunc int bpf_list_push_front(struct bpf_list_head *head,
> return __bpf_list_add(n, head, false, meta ? meta->record : NULL, off);
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_list_push_front)
> __bpf_kfunc int bpf_list_push_front_impl(struct bpf_list_head *head,
> struct bpf_list_node *node,
> void *meta__ign, u64 off)
> @@ -2543,6 +2549,7 @@ __bpf_kfunc int bpf_list_push_back(struct bpf_list_head *head,
> return __bpf_list_add(n, head, true, meta ? meta->record : NULL, off);
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_list_push_back)
> __bpf_kfunc int bpf_list_push_back_impl(struct bpf_list_head *head,
> struct bpf_list_node *node,
> void *meta__ign, u64 off)
> @@ -2683,6 +2690,7 @@ __bpf_kfunc int bpf_rbtree_add(struct bpf_rb_root *root,
> return __bpf_rbtree_add(root, n, (void *)less, meta ? meta->record : NULL, off);
> }
>
> +__bpf_kfunc_mark_deprecated(bpf_rbtree_add)
> __bpf_kfunc int bpf_rbtree_add_impl(struct bpf_rb_root *root, struct bpf_rb_node *node,
> bool (less)(struct bpf_rb_node *a, const struct bpf_rb_node *b),
> void *meta__ign, u64 off)
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8de2a4e5f5de..1f3b44f47949 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -12987,6 +12987,45 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char
> static int process_bpf_exit_full(struct bpf_verifier_env *env,
> bool *do_print_state, bool exception_exit);
>
> +static void warn_for_deprecated_kfuncs(struct bpf_verifier_env *env,
> + struct bpf_kfunc_call_arg_meta *meta,
> + int insn_idx, s16 offset)
> +{
> + const struct bpf_line_info *linfo;
> + const char *replacement;
> + const struct btf_type *t;
> + struct bpf_kfunc_desc *desc;
> + const char *file;
> + int line_num;
> +
> + if (!env->prog->aux->btf)
> + return;
> +
> + t = btf_type_by_id(meta->btf, meta->func_id);
> + replacement = btf_find_decl_tag_value(meta->btf, t, -1, BPF_KFUNC_DECL_TAG_DEPRECATED);
> + if (IS_ERR(replacement) || str_is_empty(replacement))
> + return;
> +
> + desc = find_kfunc_desc(env->prog, meta->func_id, offset);
> + if (!desc || desc->warned_deprecated)
> + return;
> +
> + linfo = bpf_find_linfo(env->prog, insn_idx);
> + if (linfo) {
> + bpf_get_linfo_file_line(env->prog->aux->btf, linfo, &file, NULL, &line_num);
> + warn(env, "%s:%d (insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> + file, line_num, insn_idx, meta->func_name);
> + } else {
> + warn(env, "(insn #%d) uses deprecated kfunc %s(), which will be removed.\n",
> + insn_idx, meta->func_name);
> + }
> +
> + warn(env, "Switch to kfunc %s() instead.\n", replacement);
> + warn(env, "For older kernels, choose the correct kfunc using bpf_ksym_exists().\n");
> +
> + desc->warned_deprecated = true;
> +}
> +
> static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> int *insn_idx_p)
> {
> @@ -13016,6 +13055,8 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>
> insn_aux->is_iter_next = bpf_is_iter_next_kfunc(&meta);
>
> + warn_for_deprecated_kfuncs(env, &meta, insn_idx, insn->off);
> +
> if (!insn->off &&
> (insn->imm == special_kfunc_list[KF_bpf_res_spin_lock] ||
> insn->imm == special_kfunc_list[KF_bpf_res_spin_lock_irqsave])) {
> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> index d876314a4d67..5a57a92a8f8c 100644
> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> @@ -1252,6 +1252,7 @@ __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, st
>
> __bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
> __bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
> +__bpf_kfunc_mark_deprecated(bpf_kfunc_implicit_arg_legacy)
> __bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, struct bpf_prog_aux *aux);
>
> /* hook targets */
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation
2026-04-20 18:15 ` David Faust
@ 2026-04-20 18:19 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-20 18:19 UTC (permalink / raw)
To: David Faust
Cc: bpf, Vineet Gupta, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Eduard Zingerman,
Ihor Solodrai, kkd, kernel-team
On Mon, 20 Apr 2026 at 20:15, David Faust <david.faust@oracle.com> wrote:
>
>
>
> On 4/18/26 10:16, Kumar Kartikeya Dwivedi wrote:
> > Documentation for KF_DEPRECATED documents the expectations and
> > recommendations for deprecating existing kfuncs. Until now, such a flag
> > never materialized. Unlike other kfunc flags, more context needs to be
> > carried with such a flag, thus we introduce __bpf_kfunc_mark_deprecated
> > as a replacement for the (non-existent) kfunc flag which takes a
> > replacement kfunc to provide recommendations to the user.
> >
> > Apply it to all existing impl-suffixed kfuncs that are preserved for
> > backwards compatibility for a few kernel releases, after we introduced
> > versions with KF_IMPLICIT_ARGS.
> >
> > Note that this is only supported by LLVM-compiled kernels as of writing,
> > since GCC does not support for BTF declaration tags.
>
> FYI support in GCC for BTF tags (both declaration and type) has been merged
> and will be available in gcc 16:
>
> https://inbox.sourceware.org/gcc-patches/f8ef5eca-4f1c-4efb-96a9-c65533e4573a@oracle.com/
>
Great news, thanks for letting me know! Hopefully, once this is out
and widespread, we'll be able to completely drop the ugly double
underscored suffixes for kfunc arguments by reusing declaration tags
there as well.
> We have been using it successfully with the selftest harness' __failure
> __msg etc. macros with gcc master.
>
> >
> [...]
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads
2026-04-18 17:16 [PATCH bpf-next v3 0/4] Add support to emit verifier warnings Kumar Kartikeya Dwivedi
2026-04-18 17:16 ` [PATCH bpf-next v3 1/4] bpf: Add support for verifier warning messages Kumar Kartikeya Dwivedi
2026-04-18 17:16 ` [PATCH bpf-next v3 2/4] bpf: Introduce __bpf_kfunc_mark_deprecated annotation Kumar Kartikeya Dwivedi
@ 2026-04-18 17:16 ` Kumar Kartikeya Dwivedi
2026-04-18 18:35 ` sashiko-bot
2026-04-20 13:57 ` Mykyta Yatsenko
2026-04-18 17:16 ` [PATCH bpf-next v3 4/4] selftests/bpf: Test verifier warning logging Kumar Kartikeya Dwivedi
3 siblings, 2 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 17:16 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
On kernels supporting BPF_LOG_LEVEL_WARN, have libbpf request verifier
warnings for BPF object program loads by ORing in the warning bit. This
keeps the existing log_level=0 retry semantics for verbose logs, while
still surfacing verifier warnings for successful loads.
When libbpf owns the log buffer, use a small buffer for warning-only
loads and flush captured warnings through the print callback. Detect
support by probing for BPF_FEAT_VERIFIER_WARNINGS in enum bpf_features,
and document the updated kernel_log_level behavior.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
tools/lib/bpf/features.c | 56 +++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.c | 25 ++++++++++-----
tools/lib/bpf/libbpf.h | 7 +++--
tools/lib/bpf/libbpf_internal.h | 2 ++
4 files changed, 80 insertions(+), 10 deletions(-)
diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c
index 4f19a0d79b0c..78f07866db12 100644
--- a/tools/lib/bpf/features.c
+++ b/tools/lib/bpf/features.c
@@ -208,6 +208,59 @@ static int probe_kern_btf_type_tag(int token_fd)
strs, sizeof(strs), token_fd));
}
+static bool btf_type_has_enum_value(const struct btf *btf, const struct btf_type *t,
+ const char *value_name)
+{
+ int i, vlen = btf_vlen(t);
+
+ if (btf_is_enum(t)) {
+ const struct btf_enum *e = btf_enum(t);
+
+ for (i = 0; i < vlen; i++, e++) {
+ if (strcmp(btf__name_by_offset(btf, e->name_off), value_name) == 0)
+ return true;
+ }
+ } else if (btf_is_enum64(t)) {
+ const struct btf_enum64 *e = btf_enum64(t);
+
+ for (i = 0; i < vlen; i++, e++) {
+ if (strcmp(btf__name_by_offset(btf, e->name_off), value_name) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int probe_kern_verifier_warnings(int token_fd)
+{
+ const struct btf_type *t;
+ struct btf *btf;
+ bool found = false;
+ __s32 type_id;
+
+ (void)token_fd;
+
+ btf = btf__load_vmlinux_btf();
+ if (libbpf_get_error(btf))
+ return 0;
+
+ type_id = btf__find_by_name_kind(btf, "bpf_features", BTF_KIND_ENUM);
+ if (type_id < 0)
+ type_id = btf__find_by_name_kind(btf, "bpf_features", BTF_KIND_ENUM64);
+ if (type_id < 0) {
+ btf__free(btf);
+ return 0;
+ }
+
+ t = btf__type_by_id(btf, type_id);
+ if (t)
+ found = btf_type_has_enum_value(btf, t, "BPF_FEAT_VERIFIER_WARNINGS");
+
+ btf__free(btf);
+ return found;
+}
+
static int probe_kern_array_mmap(int token_fd)
{
LIBBPF_OPTS(bpf_map_create_opts, opts,
@@ -669,6 +722,9 @@ static struct kern_feature_desc {
[FEAT_BTF_TYPE_TAG] = {
"BTF_KIND_TYPE_TAG support", probe_kern_btf_type_tag,
},
+ [FEAT_VERIFIER_WARNINGS] = {
+ "BPF_LOG_LEVEL_WARN verifier warnings", probe_kern_verifier_warnings,
+ },
[FEAT_MEMCG_ACCOUNT] = {
"memcg-based memory accounting", probe_memcg_account,
},
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3a80a018fc7d..c7b00c097698 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -7841,12 +7841,17 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
{
LIBBPF_OPTS(bpf_prog_load_opts, load_attr);
const char *prog_name = NULL;
+ const size_t warn_log_buf_size = 4096;
size_t log_buf_size = 0;
char *log_buf = NULL, *tmp;
bool own_log_buf = true;
__u32 log_level = prog->log_level;
+ bool supports_verifier_warnings = kernel_supports(obj, FEAT_VERIFIER_WARNINGS);
int ret, err;
+ if (supports_verifier_warnings)
+ log_level |= 16;
+
/* Be more helpful by rejecting programs that can't be validated early
* with more meaningful and actionable error message.
*/
@@ -7921,10 +7926,9 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
}
retry_load:
- /* if log_level is zero, we don't request logs initially even if
- * custom log_buf is specified; if the program load fails, then we'll
- * bump log_level to 1 and use either custom log_buf or we'll allocate
- * our own and retry the load to get details on what failed
+ /* If verifier warning logging is supported, we can request warnings
+ * even without verbose logging. If the program load fails, retry with
+ * log_level=1 to get details on what failed.
*/
if (log_level) {
if (prog->log_buf) {
@@ -7936,7 +7940,9 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
log_buf_size = obj->log_size;
own_log_buf = false;
} else {
- log_buf_size = max((size_t)BPF_LOG_BUF_SIZE, log_buf_size * 2);
+ log_buf_size = max(log_level == 16 ? warn_log_buf_size :
+ (size_t)BPF_LOG_BUF_SIZE,
+ log_buf_size * 2);
tmp = realloc(log_buf, log_buf_size);
if (!tmp) {
ret = -ENOMEM;
@@ -7954,7 +7960,10 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
ret = bpf_prog_load(prog->type, prog_name, license, insns, insns_cnt, &load_attr);
if (ret >= 0) {
- if (log_level && own_log_buf) {
+ if (log_level == 16 && load_attr.log_true_size && own_log_buf) {
+ pr_warn("prog '%s': -- BEGIN PROG LOAD WARNINGS --\n%s-- END PROG LOAD WARNINGS --\n",
+ prog->name, log_buf);
+ } else if (log_level && own_log_buf) {
pr_debug("prog '%s': -- BEGIN PROG LOAD LOG --\n%s-- END PROG LOAD LOG --\n",
prog->name, log_buf);
}
@@ -7981,8 +7990,8 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
goto out;
}
- if (log_level == 0) {
- log_level = 1;
+ if (log_level == (supports_verifier_warnings ? 16 : 0)) {
+ log_level = 1 | (supports_verifier_warnings ? 16 : 0);
goto retry_load;
}
/* On ENOSPC, increase log buffer size and retry, unless custom
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index bba4e8464396..598c6daa822b 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -194,8 +194,11 @@ struct bpf_object_open_opts {
/*
* Log level can be set independently from log buffer. Log_level=0
* means that libbpf will attempt loading BTF or program without any
- * logging requested, but will retry with either its own or custom log
- * buffer, if provided, and log_level=1 on any error.
+ * verbose logging requested, but will retry with either its own or
+ * custom log buffer, if provided, and log_level=1 on any error. On
+ * kernels supporting verifier warning logging, libbpf will also
+ * request warning messages for successful program loads by default
+ * when log_level=0.
* And vice versa, setting log_level>0 will request BTF or prog
* loading with verbose log from the first attempt (and as such also
* for successfully loaded BTF or program), and the actual log buffer
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 3781c45b46d3..b2460982d6ca 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -378,6 +378,8 @@ enum kern_feature_id {
FEAT_BTF_DECL_TAG,
/* BTF_KIND_TYPE_TAG support */
FEAT_BTF_TYPE_TAG,
+ /* Kernel supports BPF_LOG_LEVEL_WARN for verifier warnings */
+ FEAT_VERIFIER_WARNINGS,
/* memcg-based accounting for BPF maps and progs */
FEAT_MEMCG_ACCOUNT,
/* BPF cookie (bpf_get_attach_cookie() BPF helper) support */
--
2.52.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads
2026-04-18 17:16 ` [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads Kumar Kartikeya Dwivedi
@ 2026-04-18 18:35 ` sashiko-bot
2026-04-18 20:38 ` Kumar Kartikeya Dwivedi
2026-04-20 13:57 ` Mykyta Yatsenko
1 sibling, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-04-18 18:35 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi; +Cc: bpf
Sashiko AI review found 3 potential issue(s):
- [High] Valid BPF programs fail to load when a custom log buffer is too small to fit warnings.
- [High] Massive performance and memory regression for valid programs generating warnings.
- [Low] Empty debug logs are printed for every successful load without warnings.
--
commit ba31f1c82128deb94a4d629517c67a68ad69c567
Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
libbpf: Request verifier warnings for object loads
On kernels supporting BPF_LOG_LEVEL_WARN, have libbpf request verifier
warnings for BPF object program loads by ORing in the warning bit. This
keeps the existing log_level=0 retry semantics for verbose logs, while
still surfacing verifier warnings for successful loads.
When libbpf owns the log buffer, use a small buffer for warning-only
loads and flush captured warnings through the print callback. Detect
support by probing for BPF_FEAT_VERIFIER_WARNINGS in enum bpf_features,
and document the updated kernel_log_level behavior.
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 3a80a018fc7d5..c7b00c0976983 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
[ ... ]
> @@ -7936,7 +7940,9 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> log_buf_size = obj->log_size;
> own_log_buf = false;
> } else {
> - log_buf_size = max((size_t)BPF_LOG_BUF_SIZE, log_buf_size * 2);
> + log_buf_size = max(log_level == 16 ? warn_log_buf_size :
> + (size_t)BPF_LOG_BUF_SIZE,
> + log_buf_size * 2);
> tmp = realloc(log_buf, log_buf_size);
If a valid program generates more than 4096 bytes (warn_log_buf_size) of
warnings, won't the kernel's bpf_vlog_finalize() detect the log truncation
and return -ENOSPC, causing bpf_prog_load() to fail?
[ ... ]
> ret = bpf_prog_load(prog->type, prog_name, license, insns, insns_cnt, &load_attr);
> if (ret >= 0) {
> - if (log_level && own_log_buf) {
> + if (log_level == 16 && load_attr.log_true_size && own_log_buf) {
> + pr_warn("prog '%s': -- BEGIN PROG LOAD WARNINGS --\n%s-- END PROG LOAD WARNINGS --\n",
> + prog->name, log_buf);
> + } else if (log_level && own_log_buf) {
If the program loads successfully and generates no warnings, load_attr.log_true_size
will be 0. Doesn't this skip the first check and fall through to the else-if,
causing libbpf to unconditionally print an empty debug log for every successful load?
> pr_debug("prog '%s': -- BEGIN PROG LOAD LOG --\n%s-- END PROG LOAD LOG --\n",
> prog->name, log_buf);
> }
[ ... ]
> @@ -7981,8 +7990,8 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> goto out;
> }
>
> - if (log_level == 0) {
> - log_level = 1;
> + if (log_level == (supports_verifier_warnings ? 16 : 0)) {
> + log_level = 1 | (supports_verifier_warnings ? 16 : 0);
> goto retry_load;
> }
If the kernel returns -ENOSPC because the warnings did not fit in the buffer,
we hit this retry logic and bump log_level to 17 (BPF_LOG_LEVEL1 | BPF_LOG_LEVEL_WARN).
If the user provided a custom log buffer (own_log_buf == false), libbpf will not
resize it before retrying. Won't the second attempt also fail with -ENOSPC,
causing a valid program to fail to load simply because its warnings didn't fit
the custom buffer?
Additionally, if own_log_buf == true and we failed because the warnings exceeded
the initial 4096-byte buffer, retrying with log_level = 17 will allocate a 16MB
buffer and trigger a full instruction-by-instruction verification trace. Won't
this cause a massive CPU and memory regression just to load a valid program?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=3
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads
2026-04-18 18:35 ` sashiko-bot
@ 2026-04-18 20:38 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 20:38 UTC (permalink / raw)
To: sashiko; +Cc: bpf
On Sat, 18 Apr 2026 at 19:35, <sashiko-bot@kernel.org> wrote:
>
> Sashiko AI review found 3 potential issue(s):
> - [High] Valid BPF programs fail to load when a custom log buffer is too small to fit warnings.
> - [High] Massive performance and memory regression for valid programs generating warnings.
> - [Low] Empty debug logs are printed for every successful load without warnings.
> --
>
> commit ba31f1c82128deb94a4d629517c67a68ad69c567
> Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>
> libbpf: Request verifier warnings for object loads
>
> On kernels supporting BPF_LOG_LEVEL_WARN, have libbpf request verifier
> warnings for BPF object program loads by ORing in the warning bit. This
> keeps the existing log_level=0 retry semantics for verbose logs, while
> still surfacing verifier warnings for successful loads.
>
> When libbpf owns the log buffer, use a small buffer for warning-only
> loads and flush captured warnings through the print callback. Detect
> support by probing for BPF_FEAT_VERIFIER_WARNINGS in enum bpf_features,
> and document the updated kernel_log_level behavior.
>
> > diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> > index 3a80a018fc7d5..c7b00c0976983 100644
> > --- a/tools/lib/bpf/libbpf.c
> > +++ b/tools/lib/bpf/libbpf.c
>
> [ ... ]
>
> > @@ -7936,7 +7940,9 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> > log_buf_size = obj->log_size;
> > own_log_buf = false;
> > } else {
> > - log_buf_size = max((size_t)BPF_LOG_BUF_SIZE, log_buf_size * 2);
> > + log_buf_size = max(log_level == 16 ? warn_log_buf_size :
> > + (size_t)BPF_LOG_BUF_SIZE,
> > + log_buf_size * 2);
> > tmp = realloc(log_buf, log_buf_size);
>
> If a valid program generates more than 4096 bytes (warn_log_buf_size) of
> warnings, won't the kernel's bpf_vlog_finalize() detect the log truncation
> and return -ENOSPC, causing bpf_prog_load() to fail?
Good point, will check this corner case.
That said, we only warn once for every kfunc, so it would require 4096
unique kfunc calls.
In any case, will take a look.
>
> [ ... ]
>
> > ret = bpf_prog_load(prog->type, prog_name, license, insns, insns_cnt, &load_attr);
> > if (ret >= 0) {
> > - if (log_level && own_log_buf) {
> > + if (log_level == 16 && load_attr.log_true_size && own_log_buf) {
> > + pr_warn("prog '%s': -- BEGIN PROG LOAD WARNINGS --\n%s-- END PROG LOAD WARNINGS --\n",
> > + prog->name, log_buf);
> > + } else if (log_level && own_log_buf) {
>
> If the program loads successfully and generates no warnings, load_attr.log_true_size
> will be 0. Doesn't this skip the first check and fall through to the else-if,
> causing libbpf to unconditionally print an empty debug log for every successful load?
Hmm, good point, we should strip BPF_LOG_LEVEL_WARN for this test.
We will always set |= 16 on newer kernels, otherwise, we keep zero as
the default log level. Thus, the second branch is hit only if the log
level is non-zero.
>
> > pr_debug("prog '%s': -- BEGIN PROG LOAD LOG --\n%s-- END PROG LOAD LOG --\n",
> > prog->name, log_buf);
> > }
>
> [ ... ]
>
> > @@ -7981,8 +7990,8 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> > goto out;
> > }
> >
> > - if (log_level == 0) {
> > - log_level = 1;
> > + if (log_level == (supports_verifier_warnings ? 16 : 0)) {
> > + log_level = 1 | (supports_verifier_warnings ? 16 : 0);
> > goto retry_load;
> > }
>
> If the kernel returns -ENOSPC because the warnings did not fit in the buffer,
> we hit this retry logic and bump log_level to 17 (BPF_LOG_LEVEL1 | BPF_LOG_LEVEL_WARN).
>
> If the user provided a custom log buffer (own_log_buf == false), libbpf will not
> resize it before retrying. Won't the second attempt also fail with -ENOSPC,
> causing a valid program to fail to load simply because its warnings didn't fit
> the custom buffer?
>
> Additionally, if own_log_buf == true and we failed because the warnings exceeded
> the initial 4096-byte buffer, retrying with log_level = 17 will allocate a 16MB
> buffer and trigger a full instruction-by-instruction verification trace. Won't
> this cause a massive CPU and memory regression just to load a valid program?
Sounds similar to the case earlier, I will study it while addressing that.
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=3
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads
2026-04-18 17:16 ` [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads Kumar Kartikeya Dwivedi
2026-04-18 18:35 ` sashiko-bot
@ 2026-04-20 13:57 ` Mykyta Yatsenko
2026-04-20 15:23 ` Kumar Kartikeya Dwivedi
1 sibling, 1 reply; 22+ messages in thread
From: Mykyta Yatsenko @ 2026-04-20 13:57 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
On 4/18/26 6:16 PM, Kumar Kartikeya Dwivedi wrote:
> On kernels supporting BPF_LOG_LEVEL_WARN, have libbpf request verifier
> warnings for BPF object program loads by ORing in the warning bit. This
> keeps the existing log_level=0 retry semantics for verbose logs, while
> still surfacing verifier warnings for successful loads.
>
> When libbpf owns the log buffer, use a small buffer for warning-only
> loads and flush captured warnings through the print callback. Detect
> support by probing for BPF_FEAT_VERIFIER_WARNINGS in enum bpf_features,
> and document the updated kernel_log_level behavior.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
> tools/lib/bpf/features.c | 56 +++++++++++++++++++++++++++++++++
> tools/lib/bpf/libbpf.c | 25 ++++++++++-----
> tools/lib/bpf/libbpf.h | 7 +++--
> tools/lib/bpf/libbpf_internal.h | 2 ++
> 4 files changed, 80 insertions(+), 10 deletions(-)
>
> diff --git a/tools/lib/bpf/features.c b/tools/lib/bpf/features.c
> index 4f19a0d79b0c..78f07866db12 100644
> --- a/tools/lib/bpf/features.c
> +++ b/tools/lib/bpf/features.c
> @@ -208,6 +208,59 @@ static int probe_kern_btf_type_tag(int token_fd)
> strs, sizeof(strs), token_fd));
> }
>
> +static bool btf_type_has_enum_value(const struct btf *btf, const struct btf_type *t,
> + const char *value_name)
> +{
> + int i, vlen = btf_vlen(t);
> +
> + if (btf_is_enum(t)) {
> + const struct btf_enum *e = btf_enum(t);
> +
> + for (i = 0; i < vlen; i++, e++) {
> + if (strcmp(btf__name_by_offset(btf, e->name_off), value_name) == 0)
> + return true;
> + }
> + } else if (btf_is_enum64(t)) {
> + const struct btf_enum64 *e = btf_enum64(t);
> +
> + for (i = 0; i < vlen; i++, e++) {
> + if (strcmp(btf__name_by_offset(btf, e->name_off), value_name) == 0)
> + return true;
> + }
> + }
> +
> + return false;
> +}
> +
> +static int probe_kern_verifier_warnings(int token_fd)
> +{
> + const struct btf_type *t;
> + struct btf *btf;
> + bool found = false;
> + __s32 type_id;
> +
> + (void)token_fd;
> +
> + btf = btf__load_vmlinux_btf();
> + if (libbpf_get_error(btf))
> + return 0;
> +
> + type_id = btf__find_by_name_kind(btf, "bpf_features", BTF_KIND_ENUM);
> + if (type_id < 0)
> + type_id = btf__find_by_name_kind(btf, "bpf_features", BTF_KIND_ENUM64);
> + if (type_id < 0) {
> + btf__free(btf);
> + return 0;
> + }
> +
> + t = btf__type_by_id(btf, type_id);
> + if (t)
> + found = btf_type_has_enum_value(btf, t, "BPF_FEAT_VERIFIER_WARNINGS");
> +
> + btf__free(btf);
> + return found;
> +}
> +
> static int probe_kern_array_mmap(int token_fd)
> {
> LIBBPF_OPTS(bpf_map_create_opts, opts,
> @@ -669,6 +722,9 @@ static struct kern_feature_desc {
> [FEAT_BTF_TYPE_TAG] = {
> "BTF_KIND_TYPE_TAG support", probe_kern_btf_type_tag,
> },
> + [FEAT_VERIFIER_WARNINGS] = {
> + "BPF_LOG_LEVEL_WARN verifier warnings", probe_kern_verifier_warnings,
> + },
> [FEAT_MEMCG_ACCOUNT] = {
> "memcg-based memory accounting", probe_memcg_account,
> },
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 3a80a018fc7d..c7b00c097698 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -7841,12 +7841,17 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> {
> LIBBPF_OPTS(bpf_prog_load_opts, load_attr);
> const char *prog_name = NULL;
> + const size_t warn_log_buf_size = 4096;
> size_t log_buf_size = 0;
> char *log_buf = NULL, *tmp;
> bool own_log_buf = true;
> __u32 log_level = prog->log_level;
> + bool supports_verifier_warnings = kernel_supports(obj, FEAT_VERIFIER_WARNINGS);
> int ret, err;
>
> + if (supports_verifier_warnings)
> + log_level |= 16;
> +
> /* Be more helpful by rejecting programs that can't be validated early
> * with more meaningful and actionable error message.
> */
> @@ -7921,10 +7926,9 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> }
>
> retry_load:
> - /* if log_level is zero, we don't request logs initially even if
> - * custom log_buf is specified; if the program load fails, then we'll
> - * bump log_level to 1 and use either custom log_buf or we'll allocate
> - * our own and retry the load to get details on what failed
> + /* If verifier warning logging is supported, we can request warnings
> + * even without verbose logging. If the program load fails, retry with
> + * log_level=1 to get details on what failed.
> */
> if (log_level) {
> if (prog->log_buf) {
> @@ -7936,7 +7940,9 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> log_buf_size = obj->log_size;
> own_log_buf = false;
> } else {
> - log_buf_size = max((size_t)BPF_LOG_BUF_SIZE, log_buf_size * 2);
> + log_buf_size = max(log_level == 16 ? warn_log_buf_size :
> + (size_t)BPF_LOG_BUF_SIZE,
> + log_buf_size * 2);
> tmp = realloc(log_buf, log_buf_size);
> if (!tmp) {
> ret = -ENOMEM;
> @@ -7954,7 +7960,10 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
>
> ret = bpf_prog_load(prog->type, prog_name, license, insns, insns_cnt, &load_attr);
> if (ret >= 0) {
> - if (log_level && own_log_buf) {
> + if (log_level == 16 && load_attr.log_true_size && own_log_buf) {
> + pr_warn("prog '%s': -- BEGIN PROG LOAD WARNINGS --\n%s-- END PROG LOAD WARNINGS --\n",
> + prog->name, log_buf);
I'm not sure if this block makes sense to me: we only print warnings
banner is warnings is the only requested log type, but we print no
banner if warnings are requested with other log level, also different
log_levels: pr_warn() vs pr_debug().
> + } else if (log_level && own_log_buf) {
> pr_debug("prog '%s': -- BEGIN PROG LOAD LOG --\n%s-- END PROG LOAD LOG --\n",
> prog->name, log_buf);
> }
> @@ -7981,8 +7990,8 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> goto out;
> }
>
> - if (log_level == 0) {
> - log_level = 1;
> + if (log_level == (supports_verifier_warnings ? 16 : 0)) {
> + log_level = 1 | (supports_verifier_warnings ? 16 : 0);
> goto retry_load;
> }
> /* On ENOSPC, increase log buffer size and retry, unless custom
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index bba4e8464396..598c6daa822b 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -194,8 +194,11 @@ struct bpf_object_open_opts {
> /*
> * Log level can be set independently from log buffer. Log_level=0
> * means that libbpf will attempt loading BTF or program without any
> - * logging requested, but will retry with either its own or custom log
> - * buffer, if provided, and log_level=1 on any error.
> + * verbose logging requested, but will retry with either its own or
> + * custom log buffer, if provided, and log_level=1 on any error. On
> + * kernels supporting verifier warning logging, libbpf will also
> + * request warning messages for successful program loads by default
> + * when log_level=0.
> * And vice versa, setting log_level>0 will request BTF or prog
> * loading with verbose log from the first attempt (and as such also
> * for successfully loaded BTF or program), and the actual log buffer
> diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
> index 3781c45b46d3..b2460982d6ca 100644
> --- a/tools/lib/bpf/libbpf_internal.h
> +++ b/tools/lib/bpf/libbpf_internal.h
> @@ -378,6 +378,8 @@ enum kern_feature_id {
> FEAT_BTF_DECL_TAG,
> /* BTF_KIND_TYPE_TAG support */
> FEAT_BTF_TYPE_TAG,
> + /* Kernel supports BPF_LOG_LEVEL_WARN for verifier warnings */
> + FEAT_VERIFIER_WARNINGS,
> /* memcg-based accounting for BPF maps and progs */
> FEAT_MEMCG_ACCOUNT,
> /* BPF cookie (bpf_get_attach_cookie() BPF helper) support */
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads
2026-04-20 13:57 ` Mykyta Yatsenko
@ 2026-04-20 15:23 ` Kumar Kartikeya Dwivedi
2026-04-20 15:49 ` Alexei Starovoitov
0 siblings, 1 reply; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-20 15:23 UTC (permalink / raw)
To: Mykyta Yatsenko
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
On Mon, 20 Apr 2026 at 15:57, Mykyta Yatsenko
<mykyta.yatsenko5@gmail.com> wrote:
>
> On 4/18/26 6:16 PM, Kumar Kartikeya Dwivedi wrote:
> > On kernels supporting BPF_LOG_LEVEL_WARN, have libbpf request verifier
> > warnings for BPF object program loads by ORing in the warning bit. This
> > keeps the existing log_level=0 retry semantics for verbose logs, while
> > still surfacing verifier warnings for successful loads.
> >
> > When libbpf owns the log buffer, use a small buffer for warning-only
> > loads and flush captured warnings through the print callback. Detect
> > support by probing for BPF_FEAT_VERIFIER_WARNINGS in enum bpf_features,
> > and document the updated kernel_log_level behavior.
> >
> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> > ---
> [...]
> > @@ -7954,7 +7960,10 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
> >
> > ret = bpf_prog_load(prog->type, prog_name, license, insns, insns_cnt, &load_attr);
> > if (ret >= 0) {
> > - if (log_level && own_log_buf) {
> > + if (log_level == 16 && load_attr.log_true_size && own_log_buf) {
> > + pr_warn("prog '%s': -- BEGIN PROG LOAD WARNINGS --\n%s-- END PROG LOAD WARNINGS --\n",
> > + prog->name, log_buf);
>
> I'm not sure if this block makes sense to me: we only print warnings
> banner is warnings is the only requested log type, but we print no
> banner if warnings are requested with other log level, also different
> log_levels: pr_warn() vs pr_debug().
>
With other log levels, it becomes part of regular verifier output, if
requested. By default, we want to print them on success at a higher
log level upon success so they appear, even if the user didn't request
verbose logs in the log buffer.
Since the default case only generates warnings, we change the banner,
but keep it as-is for other scenarios. Does that make sense?
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads
2026-04-20 15:23 ` Kumar Kartikeya Dwivedi
@ 2026-04-20 15:49 ` Alexei Starovoitov
0 siblings, 0 replies; 22+ messages in thread
From: Alexei Starovoitov @ 2026-04-20 15:49 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, Mykyta Yatsenko
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
On Mon Apr 20, 2026 at 8:23 AM PDT, Kumar Kartikeya Dwivedi wrote:
> On Mon, 20 Apr 2026 at 15:57, Mykyta Yatsenko
> <mykyta.yatsenko5@gmail.com> wrote:
>>
>> On 4/18/26 6:16 PM, Kumar Kartikeya Dwivedi wrote:
>> > On kernels supporting BPF_LOG_LEVEL_WARN, have libbpf request verifier
>> > warnings for BPF object program loads by ORing in the warning bit. This
>> > keeps the existing log_level=0 retry semantics for verbose logs, while
>> > still surfacing verifier warnings for successful loads.
>> >
>> > When libbpf owns the log buffer, use a small buffer for warning-only
>> > loads and flush captured warnings through the print callback. Detect
>> > support by probing for BPF_FEAT_VERIFIER_WARNINGS in enum bpf_features,
>> > and document the updated kernel_log_level behavior.
>> >
>> > Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>> > ---
>> [...]
>> > @@ -7954,7 +7960,10 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog
>> >
>> > ret = bpf_prog_load(prog->type, prog_name, license, insns, insns_cnt, &load_attr);
>> > if (ret >= 0) {
>> > - if (log_level && own_log_buf) {
>> > + if (log_level == 16 && load_attr.log_true_size && own_log_buf) {
>> > + pr_warn("prog '%s': -- BEGIN PROG LOAD WARNINGS --\n%s-- END PROG LOAD WARNINGS --\n",
>> > + prog->name, log_buf);
>>
>> I'm not sure if this block makes sense to me: we only print warnings
>> banner is warnings is the only requested log type, but we print no
>> banner if warnings are requested with other log level, also different
>> log_levels: pr_warn() vs pr_debug().
>>
>
> With other log levels, it becomes part of regular verifier output, if
> requested. By default, we want to print them on success at a higher
> log level upon success so they appear, even if the user didn't request
> verbose logs in the log buffer.
> Since the default case only generates warnings, we change the banner,
> but keep it as-is for other scenarios. Does that make sense?
I'm not a fan of banner either.
Screaming BEGIN and END is imo too verbose. I would skip it altogether.
And probably would have removed it from existing 2 places in libbpf.
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH bpf-next v3 4/4] selftests/bpf: Test verifier warning logging
2026-04-18 17:16 [PATCH bpf-next v3 0/4] Add support to emit verifier warnings Kumar Kartikeya Dwivedi
` (2 preceding siblings ...)
2026-04-18 17:16 ` [PATCH bpf-next v3 3/4] libbpf: Request verifier warnings for object loads Kumar Kartikeya Dwivedi
@ 2026-04-18 17:16 ` Kumar Kartikeya Dwivedi
2026-04-18 18:45 ` sashiko-bot
3 siblings, 1 reply; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 17:16 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Ihor Solodrai, kkd,
kernel-team
Add coverage for explicit verifier warning logging and for libbpf's
default warning flush path while keeping the warning bit private to the
test.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../bpf/prog_tests/kfunc_implicit_args.c | 92 +++++++++++++++++++
1 file changed, 92 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
index 5e4793c9c29a..f92a27d76455 100644
--- a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
@@ -4,7 +4,99 @@
#include <test_progs.h>
#include "kfunc_implicit_args.skel.h"
+#define TEST_BPF_LOG_LEVEL_WARN (1U << 4)
+
+static size_t libbpf_log_pos;
+static char libbpf_log_buf[4096];
+static bool libbpf_log_error;
+
+static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt, va_list args)
+{
+ int emitted_cnt;
+ size_t left_cnt;
+
+ (void)level;
+
+ left_cnt = sizeof(libbpf_log_buf) - libbpf_log_pos;
+ emitted_cnt = vsnprintf(libbpf_log_buf + libbpf_log_pos, left_cnt, fmt, args);
+ if (emitted_cnt < 0 || emitted_cnt + 1 > left_cnt) {
+ libbpf_log_error = true;
+ return 0;
+ }
+
+ libbpf_log_pos += emitted_cnt;
+ return 0;
+}
+
+static void select_legacy_impl_prog(struct kfunc_implicit_args *skel)
+{
+ struct bpf_program *prog;
+
+ bpf_object__for_each_program(prog, skel->obj)
+ bpf_program__set_autoload(prog,
+ prog == skel->progs.test_kfunc_implicit_arg_legacy_impl);
+}
+
+static void test_warn_bit(void)
+{
+ struct kfunc_implicit_args *skel;
+ char log_buf[4096] = {};
+ int err;
+
+ skel = kfunc_implicit_args__open();
+ if (!ASSERT_OK_PTR(skel, "skel_open"))
+ return;
+
+ select_legacy_impl_prog(skel);
+ bpf_program__set_log_buf(skel->progs.test_kfunc_implicit_arg_legacy_impl,
+ log_buf, sizeof(log_buf));
+ bpf_program__set_log_level(skel->progs.test_kfunc_implicit_arg_legacy_impl,
+ TEST_BPF_LOG_LEVEL_WARN);
+
+ err = kfunc_implicit_args__load(skel);
+ ASSERT_OK(err, "skel_load");
+ ASSERT_OK_PTR(strstr(log_buf, "uses deprecated kfunc bpf_kfunc_implicit_arg_legacy_impl()"),
+ "warning_msg");
+ ASSERT_NULL(strstr(log_buf, "processed "), "no_verbose_log");
+
+ kfunc_implicit_args__destroy(skel);
+}
+
+static void test_default_warning_flush(void)
+{
+ libbpf_print_fn_t old_print_cb = libbpf_set_print(libbpf_print_cb);
+ struct kfunc_implicit_args *skel = NULL;
+ int err;
+
+ libbpf_log_pos = 0;
+ libbpf_log_buf[0] = '\0';
+ libbpf_log_error = false;
+
+ skel = kfunc_implicit_args__open();
+ if (!ASSERT_OK_PTR(skel, "skel_open"))
+ goto cleanup;
+
+ select_legacy_impl_prog(skel);
+
+ err = kfunc_implicit_args__load(skel);
+ ASSERT_OK(err, "skel_load");
+ ASSERT_FALSE(libbpf_log_error, "libbpf_log_error");
+ ASSERT_OK_PTR(strstr(libbpf_log_buf, "-- BEGIN PROG LOAD WARNINGS --"), "warn_banner");
+ ASSERT_OK_PTR(strstr(libbpf_log_buf,
+ "uses deprecated kfunc bpf_kfunc_implicit_arg_legacy_impl()"),
+ "warn_msg");
+ ASSERT_NULL(strstr(libbpf_log_buf, "-- BEGIN PROG LOAD LOG --"), "no_verbose_log");
+
+cleanup:
+ kfunc_implicit_args__destroy(skel);
+ libbpf_set_print(old_print_cb);
+}
+
void test_kfunc_implicit_args(void)
{
RUN_TESTS(kfunc_implicit_args);
+ if (test__start_subtest("warn_bit"))
+ test_warn_bit();
+ if (test__start_subtest("default_warning_flush"))
+ test_default_warning_flush();
}
--
2.52.0
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 4/4] selftests/bpf: Test verifier warning logging
2026-04-18 17:16 ` [PATCH bpf-next v3 4/4] selftests/bpf: Test verifier warning logging Kumar Kartikeya Dwivedi
@ 2026-04-18 18:45 ` sashiko-bot
2026-04-18 20:39 ` Kumar Kartikeya Dwivedi
0 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-04-18 18:45 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi; +Cc: bpf
Sashiko AI review found 1 potential issue(s):
- [Medium] The custom `libbpf_print_cb` unconditionally buffers all log levels into a fixed 4096-byte buffer, which will inevitably overflow during skeleton load and silently drop the expected verifier warnings.
--
commit 602c54794881684bfce8f29591c7e680ab7d51fe
Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
selftests/bpf: Test verifier warning logging
This commit adds test coverage for explicit verifier warning logging and
libbpf's default warning flush path while keeping the warning bit private
to the test.
> diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
> index 5e4793c9c29a0..f92a27d764557 100644
> --- a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
> +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
> @@ -4,7 +4,99 @@
> #include <test_progs.h>
> #include "kfunc_implicit_args.skel.h"
>
> +#define TEST_BPF_LOG_LEVEL_WARN (1U << 4)
> +
> +static size_t libbpf_log_pos;
> +static char libbpf_log_buf[4096];
> +static bool libbpf_log_error;
> +
> +static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt, va_list args)
> +{
> + int emitted_cnt;
> + size_t left_cnt;
> +
> + (void)level;
> +
> + left_cnt = sizeof(libbpf_log_buf) - libbpf_log_pos;
> + emitted_cnt = vsnprintf(libbpf_log_buf + libbpf_log_pos, left_cnt, fmt, args);
> + if (emitted_cnt < 0 || emitted_cnt + 1 > left_cnt) {
> + libbpf_log_error = true;
> + return 0;
> + }
Since libbpf generates a high volume of LIBBPF_DEBUG and LIBBPF_INFO messages
during object load, will this unconditionally buffer all log levels and overflow
the 4096-byte buffer before the load completes?
If the buffer overflows, libbpf_log_error becomes true and subsequent messages
(including the expected verifier warnings flushed at the end of the load) will
be discarded.
Should this callback filter out non-warning levels to avoid filling the buffer
too quickly?
[ ... ]
> +static void test_default_warning_flush(void)
> +{
> + libbpf_print_fn_t old_print_cb = libbpf_set_print(libbpf_print_cb);
> + struct kfunc_implicit_args *skel = NULL;
> + int err;
> +
> + libbpf_log_pos = 0;
> + libbpf_log_buf[0] = '\0';
> + libbpf_log_error = false;
> +
> + skel = kfunc_implicit_args__open();
> + if (!ASSERT_OK_PTR(skel, "skel_open"))
> + goto cleanup;
> +
> + select_legacy_impl_prog(skel);
> +
> + err = kfunc_implicit_args__load(skel);
> + ASSERT_OK(err, "skel_load");
> + ASSERT_FALSE(libbpf_log_error, "libbpf_log_error");
Will this assertion predictably fail due to the buffer overflow mentioned
above?
> + ASSERT_OK_PTR(strstr(libbpf_log_buf, "-- BEGIN PROG LOAD WARNINGS --"), "warn_banner");
> + ASSERT_OK_PTR(strstr(libbpf_log_buf,
> + "uses deprecated kfunc bpf_kfunc_implicit_arg_legacy_impl()"),
> + "warn_msg");
> + ASSERT_NULL(strstr(libbpf_log_buf, "-- BEGIN PROG LOAD LOG --"), "no_verbose_log");
> +
> +cleanup:
> + kfunc_implicit_args__destroy(skel);
> + libbpf_set_print(old_print_cb);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=4
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH bpf-next v3 4/4] selftests/bpf: Test verifier warning logging
2026-04-18 18:45 ` sashiko-bot
@ 2026-04-18 20:39 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 22+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-18 20:39 UTC (permalink / raw)
To: sashiko; +Cc: bpf
On Sat, 18 Apr 2026 at 19:45, <sashiko-bot@kernel.org> wrote:
>
> Sashiko AI review found 1 potential issue(s):
> - [Medium] The custom `libbpf_print_cb` unconditionally buffers all log levels into a fixed 4096-byte buffer, which will inevitably overflow during skeleton load and silently drop the expected verifier warnings.
> --
>
> commit 602c54794881684bfce8f29591c7e680ab7d51fe
> Author: Kumar Kartikeya Dwivedi <memxor@gmail.com>
>
> selftests/bpf: Test verifier warning logging
>
> This commit adds test coverage for explicit verifier warning logging and
> libbpf's default warning flush path while keeping the warning bit private
> to the test.
>
> > diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
> > index 5e4793c9c29a0..f92a27d764557 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
> > @@ -4,7 +4,99 @@
> > #include <test_progs.h>
> > #include "kfunc_implicit_args.skel.h"
> >
> > +#define TEST_BPF_LOG_LEVEL_WARN (1U << 4)
> > +
> > +static size_t libbpf_log_pos;
> > +static char libbpf_log_buf[4096];
> > +static bool libbpf_log_error;
> > +
> > +static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt, va_list args)
> > +{
> > + int emitted_cnt;
> > + size_t left_cnt;
> > +
> > + (void)level;
> > +
> > + left_cnt = sizeof(libbpf_log_buf) - libbpf_log_pos;
> > + emitted_cnt = vsnprintf(libbpf_log_buf + libbpf_log_pos, left_cnt, fmt, args);
> > + if (emitted_cnt < 0 || emitted_cnt + 1 > left_cnt) {
> > + libbpf_log_error = true;
> > + return 0;
> > + }
>
> Since libbpf generates a high volume of LIBBPF_DEBUG and LIBBPF_INFO messages
> during object load, will this unconditionally buffer all log levels and overflow
> the 4096-byte buffer before the load completes?
>
> If the buffer overflows, libbpf_log_error becomes true and subsequent messages
> (including the expected verifier warnings flushed at the end of the load) will
> be discarded.
>
> Should this callback filter out non-warning levels to avoid filling the buffer
> too quickly?
Perhaps, I will check and adjust if necessary.
>
> [ ... ]
>
> > +static void test_default_warning_flush(void)
> > +{
> > + libbpf_print_fn_t old_print_cb = libbpf_set_print(libbpf_print_cb);
> > + struct kfunc_implicit_args *skel = NULL;
> > + int err;
> > +
> > + libbpf_log_pos = 0;
> > + libbpf_log_buf[0] = '\0';
> > + libbpf_log_error = false;
> > +
> > + skel = kfunc_implicit_args__open();
> > + if (!ASSERT_OK_PTR(skel, "skel_open"))
> > + goto cleanup;
> > +
> > + select_legacy_impl_prog(skel);
> > +
> > + err = kfunc_implicit_args__load(skel);
> > + ASSERT_OK(err, "skel_load");
> > + ASSERT_FALSE(libbpf_log_error, "libbpf_log_error");
>
> Will this assertion predictably fail due to the buffer overflow mentioned
> above?
No, it's a non-common case.
>
> > + ASSERT_OK_PTR(strstr(libbpf_log_buf, "-- BEGIN PROG LOAD WARNINGS --"), "warn_banner");
> > + ASSERT_OK_PTR(strstr(libbpf_log_buf,
> > + "uses deprecated kfunc bpf_kfunc_implicit_arg_legacy_impl()"),
> > + "warn_msg");
> > + ASSERT_NULL(strstr(libbpf_log_buf, "-- BEGIN PROG LOAD LOG --"), "no_verbose_log");
> > +
> > +cleanup:
> > + kfunc_implicit_args__destroy(skel);
> > + libbpf_set_print(old_print_cb);
> > +}
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260418171701.610025-1-memxor@gmail.com?part=4
^ permalink raw reply [flat|nested] 22+ messages in thread