Add a small diagnostics renderer for verifier reports and wire it into the BPF build. The initial helpers emit the common text structure: a failure header plus reusable report sections. Wrap report prose at 100 columns so Reason and Suggestion text stays readable without changing source or instruction gutters. Gate the helpers on normal verifier log output from the start, so BPF_LOG_STATS-only loads do not collect or render diagnostics. Acked-by: Eduard Zingerman Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/Makefile | 2 +- kernel/bpf/diagnostics.c | 157 +++++++++++++++++++++++++++++++++++++++ kernel/bpf/diagnostics.h | 16 ++++ 3 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 kernel/bpf/diagnostics.c create mode 100644 kernel/bpf/diagnostics.h diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 4dc41bf5780c..90255d80e5be 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -6,7 +6,7 @@ cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse endif CFLAGS_core.o += -Wno-override-init $(cflags-nogcse-yy) -obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cnum.o log.o token.o liveness.o const_fold.o +obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cnum.o log.o token.o liveness.o const_fold.o diagnostics.o obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o bpf_insn_array.o diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c new file mode 100644 index 000000000000..6d28a3481cfa --- /dev/null +++ b/kernel/bpf/diagnostics.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + +#include +#include +#include +#include +#include + +#include "diagnostics.h" + +#define CATEGORY_MEMORY_SAFETY "Memory Safety" +#define CATEGORY_REGISTER_TYPE_SAFETY "Register Type Safety" +#define CATEGORY_CALL_TYPE_SAFETY "Call Type Safety" +#define CATEGORY_RESOURCE_LIFETIME_SAFETY "Resource Lifetime Safety" +#define CATEGORY_EXECUTION_CONTEXT_SAFETY "Execution Context Safety" +#define CATEGORY_PROGRAM_STRUCTURE "Program Structure" +#define CATEGORY_POLICY "Policy" +#define CATEGORY_VERIFIER_LIMIT "Verifier Limit" +#define CATEGORY_VERIFIER_INTERNAL_ERROR "Verifier Internal Error" + +#define BPF_DIAG_TEXT_WIDTH 100 +#define BPF_DIAG_TEXT_INDENT " " + +bool bpf_diag_enabled(const struct bpf_verifier_env *env) +{ + return env->log.level & BPF_LOG_LEVEL; +} + +static void diag_log(struct bpf_verifier_env *env, const char *fmt, ...) +{ + va_list args; + + if (!bpf_diag_enabled(env)) + return; + + va_start(args, fmt); + bpf_verifier_vlog(&env->log, fmt, args); + va_end(args); +} + +static void diag_print_wrapped_prefixed(struct bpf_verifier_env *env, const char *first_prefix, + const char *next_prefix, const char *text) +{ + const char *prefix = first_prefix; + + while (*text) { + const char *line = text; + int prefix_len = strlen(prefix); + int text_width = BPF_DIAG_TEXT_WIDTH - prefix_len; + int len = 0, last_space = -1; + + if (text_width < 1) + text_width = 1; + + while (line[len] && line[len] != '\n' && len < text_width) { + if (line[len] == ' ') + last_space = len; + len++; + } + + if (line[len] && line[len] != '\n' && line[len] != ' ' && last_space > 0) + len = last_space; + + diag_log(env, "%s%.*s\n", prefix, len, line); + + text = line + len; + while (*text == ' ') + text++; + if (*text == '\n') + text++; + + prefix = next_prefix; + } +} + +static void diag_print_wrapped_text(struct bpf_verifier_env *env, const char *text) +{ + diag_print_wrapped_prefixed(env, BPF_DIAG_TEXT_INDENT, BPF_DIAG_TEXT_INDENT, text); +} + +static void diag_vprint_indented(struct bpf_verifier_env *env, const char *fmt, va_list args) +{ + char *buf; + + if (!bpf_diag_enabled(env)) + return; + + buf = kvasprintf(GFP_KERNEL_ACCOUNT, fmt, args); + if (!buf) { + diag_log(env, "%s\n", BPF_DIAG_TEXT_INDENT); + return; + } + + diag_print_wrapped_text(env, buf); + kfree(buf); +} + +void bpf_diag_report_header(struct bpf_verifier_env *env, const char *category, const char *problem) +{ + char first; + + if (!bpf_diag_enabled(env)) + return; + + category = category ?: CATEGORY_VERIFIER_INTERNAL_ERROR; + problem = problem ?: ""; + + if (!problem[0]) { + diag_log(env, "\nVerification failed: %s\n", category); + return; + } + + first = toupper(problem[0]); + diag_log(env, "\nVerification failed: %s: %c%s\n", category, first, problem + 1); +} + +static void diag_report_reason(struct bpf_verifier_env *env, const char *fmt, ...) __printf(2, 3); +static void diag_report_suggestion(struct bpf_verifier_env *env, const char *fmt, ...) + __printf(2, 3); + +static void diag_report_section(struct bpf_verifier_env *env, const char *title) +{ + if (!bpf_diag_enabled(env)) + return; + + diag_log(env, "\n%s:\n", title); +} + +static void diag_report_reason(struct bpf_verifier_env *env, const char *fmt, ...) +{ + va_list args; + + if (!bpf_diag_enabled(env)) + return; + + diag_report_section(env, "Reason"); + + va_start(args, fmt); + diag_vprint_indented(env, fmt, args); + va_end(args); +} + +static void diag_report_suggestion(struct bpf_verifier_env *env, const char *fmt, ...) +{ + va_list args; + + if (!bpf_diag_enabled(env)) + return; + + diag_report_section(env, "Suggestion"); + + va_start(args, fmt); + diag_vprint_indented(env, fmt, args); + va_end(args); + diag_log(env, "\n"); +} diff --git a/kernel/bpf/diagnostics.h b/kernel/bpf/diagnostics.h new file mode 100644 index 000000000000..be7ad5142726 --- /dev/null +++ b/kernel/bpf/diagnostics.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ + +#ifndef __BPF_DIAGNOSTICS_H +#define __BPF_DIAGNOSTICS_H + +#include +#include + +struct bpf_verifier_env; + +bool bpf_diag_enabled(const struct bpf_verifier_env *env); +void bpf_diag_report_header(struct bpf_verifier_env *env, const char *category, + const char *problem); + +#endif /* __BPF_DIAGNOSTICS_H */ -- 2.53.0