Relax conditions such that we allow log_level = 0 to 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 --- include/linux/bpf_verifier.h | 1 + kernel/bpf/log.c | 6 ++---- kernel/bpf/verifier.c | 17 +++++++++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 36bfd96d4563..c7b34236a4b5 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -795,6 +795,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 37d72b052192..c67fbbbd5768 100644 --- a/kernel/bpf/log.c +++ b/kernel/bpf/log.c @@ -18,13 +18,11 @@ static bool bpf_verifier_log_attr_valid(const struct bpf_verifier_log *log) /* ubuf and len_total should both be specified (or not) together */ if (!!log->ubuf != !!log->len_total) return false; - /* log buf without log_level is meaningless */ - if (log->ubuf && log->level == 0) - return false; if (log->level & ~BPF_LOG_MASK) return false; if (log->len_total > UINT_MAX >> 2) return false; + /* log->ubuf may be set for log->level = 0 to get warning messages. */ return true; } @@ -229,7 +227,7 @@ int bpf_vlog_finalize(struct bpf_verifier_log *log, u32 *log_size_actual) int err; *log_size_actual = 0; - if (!log || log->level == 0 || log->level == BPF_LOG_KERNEL) + if (!log || (!log->ubuf && log->level == 0) || log->level == BPF_LOG_KERNEL) return 0; if (!log->ubuf) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 594260c1f382..86d77d5f7f83 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, }; @@ -368,6 +369,17 @@ __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; + + 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, @@ -2128,7 +2140,8 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx, if (err) return err; } - if (pop_log) + /* Preserve warning-only output across branch explorations. */ + if (pop_log && !(env->warnings && env->log.level == 0)) bpf_vlog_reset(&env->log, head->log_pos); if (insn_idx) *insn_idx = head->insn_idx; @@ -25211,7 +25224,7 @@ 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_vlog_reset(&env->log, 0); free_states(env); return ret; -- 2.52.0