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. Keep reusable formatting scratch storage in the environment-owned diagnostics object. Gate the helpers on normal verifier log output from the start, so BPF_LOG_STATS-only loads do not collect or render diagnostics. Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/Makefile | 2 +- kernel/bpf/diagnostics.c | 170 +++++++++++++++++++++++++++++++++++++++ kernel/bpf/diagnostics.h | 16 ++++ 3 files changed, 187 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..a18fd5aa395d --- /dev/null +++ b/kernel/bpf/diagnostics.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + +#include +#include +#include +#include +#include + +#include "diagnostics.h" + +#define BPF_DIAG_CATEGORY_MEMORY_SAFETY "Memory Safety" +#define BPF_DIAG_CATEGORY_REGISTER_TYPE_SAFETY "Register Type Safety" +#define BPF_DIAG_CATEGORY_CALL_TYPE_SAFETY "Call Type Safety" +#define BPF_DIAG_CATEGORY_RESOURCE_LIFETIME_SAFETY "Resource Lifetime Safety" +#define BPF_DIAG_CATEGORY_EXECUTION_CONTEXT_SAFETY "Execution Context Safety" +#define BPF_DIAG_CATEGORY_PROGRAM_STRUCTURE "Program Structure" +#define BPF_DIAG_CATEGORY_POLICY "Policy" +#define BPF_DIAG_CATEGORY_VERIFIER_LIMIT "Verifier Limit" +#define BPF_DIAG_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 bpf_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 bpf_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; + + bpf_diag_log(env, "%s%.*s\n", prefix, len, line); + + text = line + len; + while (*text == ' ') + text++; + if (*text == '\n') + text++; + + prefix = next_prefix; + } +} + +static void bpf_diag_print_wrapped_text(struct bpf_verifier_env *env, + const char *text) +{ + bpf_diag_print_wrapped_prefixed(env, BPF_DIAG_TEXT_INDENT, + BPF_DIAG_TEXT_INDENT, text); +} + +static void bpf_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) { + bpf_diag_log(env, "%s\n", + BPF_DIAG_TEXT_INDENT); + return; + } + + bpf_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 ?: BPF_DIAG_CATEGORY_VERIFIER_INTERNAL_ERROR; + problem = problem ?: ""; + + if (!problem[0]) { + bpf_diag_log(env, "\nVerification failed: %s\n", category); + return; + } + + first = toupper(problem[0]); + bpf_diag_log(env, "\nVerification failed: %s: %c%s\n", category, + first, problem + 1); +} + +static void bpf_diag_report_reason(struct bpf_verifier_env *env, + const char *fmt, ...) __printf(2, 3); +static void bpf_diag_report_suggestion(struct bpf_verifier_env *env, + const char *fmt, ...) __printf(2, 3); + +static void bpf_diag_report_section(struct bpf_verifier_env *env, + const char *title) +{ + if (!bpf_diag_enabled(env)) + return; + + bpf_diag_log(env, "\n%s:\n", title); +} + +static void bpf_diag_report_reason(struct bpf_verifier_env *env, + const char *fmt, ...) +{ + va_list args; + + if (!bpf_diag_enabled(env)) + return; + + bpf_diag_report_section(env, "Reason"); + + va_start(args, fmt); + bpf_diag_vprint_indented(env, fmt, args); + va_end(args); +} + +static void bpf_diag_report_suggestion(struct bpf_verifier_env *env, + const char *fmt, ...) +{ + va_list args; + + if (!bpf_diag_enabled(env)) + return; + + bpf_diag_report_section(env, "Suggestion"); + + va_start(args, fmt); + bpf_diag_vprint_indented(env, fmt, args); + va_end(args); + bpf_diag_log(env, "\n"); +} diff --git a/kernel/bpf/diagnostics.h b/kernel/bpf/diagnostics.h new file mode 100644 index 000000000000..5fb3271530a1 --- /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