// SPDX-License-Identifier: GPL-2.0-only// Copyright (c) 2026 Meta Platforms, Inc. and affiliates. #include <linux/bpf.h>#include <linux/bpf_verifier.h>#include <linux/btf.h>#include <linux/ctype.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/seq_buf.h>#include <linux/overflow.h>#include <linux/slab.h>#include <linux/stdarg.h>#include <linux/string.h> #include "disasm.h"#include "diagnostics.h" #define REGISTER_TYPE_SAFETY "Register Type Safety"#define MEMORY_SAFETY "Memory Safety"#define RESOURCE_LIFETIME_SAFETY "Resource Lifetime Safety"#define CALL_TYPE_SAFETY "Call Type Safety"#define EXECUTION_CONTEXT_SAFETY "Execution Context Safety"#define PROGRAM_STRUCTURE "Program Structure"#define POLICY "Policy" #define BPF_DIAG_TEXT_WIDTH 100#define BPF_DIAG_TEXT_INDENT " "#define BPF_DIAG_CONTEXT 2#define BPF_DIAG_CONTEXT_CNT (1 + BPF_DIAG_CONTEXT * 2)#define BPF_DIAG_HISTORY_RENDER_MAX 64#define BPF_DIAG_SOURCE_LANE_WIDTH 88#define BPF_DIAG_TAB_WIDTH 8#define BPF_DIAG_FMT_CHUNK_SIZE (PAGE_SIZE - sizeof(struct diag_fmt_chunk))#define BPF_DIAG_FMT_BUF_SIZE 256#define BPF_DIAG_EVENT_LOG_MAX_SIZE (64U << 20)#define DISASM_LINE_LEN 160 enum bpf_diag_mod_target_kind { BPF_DIAG_MOD_TARGET_NONE, BPF_DIAG_MOD_TARGET_REG, BPF_DIAG_MOD_TARGET_STACK_ARG, BPF_DIAG_MOD_TARGET_STACK_SLOT, BPF_DIAG_MOD_TARGET_STACK_RANGE,}; struct bpf_diag_mod_target { u32 frame_id; union { struct { s16 min_off; s16 max_off; } range; u16 spi; u8 regno; u8 stack_arg; }; u8 frameno; u8 kind;}; static struct bpf_diag_mod_target diag_reg_target(u32 frame_id, u8 frameno, u8 regno){ return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_REG, .regno = regno, };} static struct bpf_diag_mod_target diag_stack_arg_target(u32 frame_id, u8 frameno, u8 slot){ return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_STACK_ARG, .stack_arg = slot, };} static struct bpf_diag_mod_target diag_stack_slot_target(u32 frame_id, u8 frameno, u16 spi){ return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_STACK_SLOT, .spi = spi, };} static struct bpf_diag_mod_target diag_stack_range_target(u32 frame_id, u8 frameno, s16 min_off, s16 max_off){ return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_STACK_RANGE, .range.min_off = min_off, .range.max_off = max_off, };} struct bpf_diag_reg_snapshot { u32 type; u32 btf_id; const struct bpf_map *map_ptr; const struct btf *btf; struct tnum var_off; struct cnum64 r64;}; enum bpf_diag_history_kind { BPF_DIAG_HISTORY_BRANCH, BPF_DIAG_HISTORY_MOD, BPF_DIAG_HISTORY_REF_ACQUIRE, BPF_DIAG_HISTORY_REF_RELEASE, BPF_DIAG_HISTORY_CONTEXT,}; struct bpf_diag_history_event { u32 insn_idx : 24; u32 kind : 8; u8 in_lineage : 1; union { struct { bool cond_true; } branch; struct { struct bpf_diag_mod_target target; struct bpf_diag_mod_target origin; struct bpf_diag_reg_snapshot old, new; u8 reason; bool origin_valid; } mod; struct { u32 ref_id; } ref; struct { u32 depth; u8 kind; bool enter; } ctx; };}; enum bpf_diag_history_scope { BPF_DIAG_HISTORY_SCOPE_REG, BPF_DIAG_HISTORY_SCOPE_STACK_ARG, BPF_DIAG_HISTORY_SCOPE_REF, BPF_DIAG_HISTORY_SCOPE_CONTEXT,}; struct bpf_diag_history_opts { enum bpf_diag_history_scope scope; u32 frame_id; u32 frameno; int regno; int stack_arg_slot; u32 ref_id; enum bpf_diag_context_kind ctx_kind; u32 ctx_depth;}; static void diag_print_history(struct bpf_verifier_env *env, const struct bpf_diag_history_opts *opts);static bool diag_target_matches(const struct bpf_diag_mod_target *event_target, const struct bpf_diag_mod_target *target);static const char *diag_context_name(enum bpf_diag_context_kind kind);struct disasm_line { char text[DISASM_LINE_LEN]; int idx; bool valid;}; struct disasm_ctx { struct bpf_verifier_env *env; struct seq_buf seq;}; struct diag_fmt_chunk { struct list_head node; struct seq_buf seq; char data[];}; struct diag_fmt_mark { struct diag_fmt_chunk *chunk; size_t len;}; struct bpf_diag_log { struct bpf_diag_history_event *events; /* Sequence number of the oldest retained event on the active path. */ u64 first_seq; u32 cnt; u32 cap; u32 head; bool growth_failed;}; struct bpf_diag_scratch { struct bpf_linfo_source source_lines[BPF_DIAG_CONTEXT_CNT]; struct disasm_line disasm_lines[BPF_DIAG_CONTEXT_CNT];}; struct bpf_diag_mod_scope { struct bpf_reg_state target_reg_snapshot; struct bpf_diag_mod_target target; struct bpf_diag_mod_target origin; enum bpf_diag_mod_reason reason; u32 insn_idx; bool active; bool origin_valid;}; struct bpf_diag { struct bpf_diag_log log; struct bpf_diag_scratch scratch; struct list_head fmt_chunks; struct bpf_diag_mod_scope mod; u32 frame_id_gen;}; bool bpf_diag_enabled(const struct bpf_verifier_env *env){ return env->log.level & BPF_LOG_LEVEL;} static void diag_write(struct bpf_verifier_env *env, const char *fmt, ...) __printf(2, 3); int bpf_diag_init(struct bpf_verifier_env *env){ if (!bpf_diag_enabled(env)) return 0; env->diag = kzalloc_obj(struct bpf_diag, GFP_KERNEL_ACCOUNT); if (!env->diag) return -ENOMEM; INIT_LIST_HEAD(&env->diag->fmt_chunks); return 0;} void bpf_diag_init_frame(struct bpf_verifier_env *env, struct bpf_func_state *state){ if (env->diag) state->diag_frame_id = ++env->diag->frame_id_gen;} static char *diag_fmt_alloc(struct bpf_verifier_env *env, size_t size){ struct bpf_diag *diag = env->diag; struct diag_fmt_chunk *chunk; size_t capacity, available; char *buf; if (!diag || !size || size > INT_MAX) return NULL; if (!list_empty(&diag->fmt_chunks)) { chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); available = seq_buf_get_buf(&chunk->seq, &buf); if (available >= size) goto commit; } capacity = max_t(size_t, BPF_DIAG_FMT_CHUNK_SIZE, size); chunk = kmalloc_flex(*chunk, data, capacity, GFP_KERNEL_ACCOUNT); if (!chunk) return NULL; seq_buf_init(&chunk->seq, chunk->data, capacity); list_add_tail(&chunk->node, &diag->fmt_chunks); available = seq_buf_get_buf(&chunk->seq, &buf); if (WARN_ON_ONCE(available < size)) return NULL; commit: seq_buf_commit(&chunk->seq, size); return buf;} char *bpf_diag_fmt_buf(struct bpf_verifier_env *env, size_t size){ char *buf; buf = diag_fmt_alloc(env, size); if (buf) buf[0] = '\0'; return buf;} const char *bpf_diag_vfmt(struct bpf_verifier_env *env, const char *fmt, va_list args){ va_list copy; char *buf; int len; va_copy(copy, args); len = vsnprintf(NULL, 0, fmt, copy); va_end(copy); if (len < 0 || len == INT_MAX) return ""; buf = diag_fmt_alloc(env, len + 1); if (buf) vsnprintf(buf, len + 1, fmt, args); return buf ?: "";} const char *bpf_diag_fmt(struct bpf_verifier_env *env, const char *fmt, ...){ const char *buf; va_list args; va_start(args, fmt); buf = bpf_diag_vfmt(env, fmt, args); va_end(args); return buf;} static struct diag_fmt_mark diag_fmt_save(struct bpf_verifier_env *env){ struct bpf_diag *diag = env->diag; struct diag_fmt_mark mark = {}; if (!diag || list_empty(&diag->fmt_chunks)) return mark; mark.chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); mark.len = mark.chunk->seq.len; return mark;} static void diag_fmt_restore(struct bpf_verifier_env *env, struct diag_fmt_mark mark){ struct bpf_diag *diag = env->diag; struct diag_fmt_chunk *chunk; if (!diag) return; while (!list_empty(&diag->fmt_chunks)) { chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); if (chunk == mark.chunk) break; list_del(&chunk->node); kfree(chunk); } if (mark.chunk) { mark.chunk->seq.len = mark.len; seq_buf_str(&mark.chunk->seq); }} void bpf_diag_free(struct bpf_verifier_env *env){ struct bpf_diag *diag = env->diag; if (!diag) return; diag_fmt_restore(env, (struct diag_fmt_mark){}); kvfree(diag->log.events); kfree(diag); env->diag = NULL;} static void diag_write(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 u64 log_end(const struct bpf_diag_log *log){ return log->first_seq + log->cnt;} static u32 log_pos(const struct bpf_diag_log *log, u32 idx){ u32 pos = log->head + idx; return pos < log->cap ? pos : pos - log->cap;} u64 bpf_diag_event_log_save(struct bpf_verifier_env *env){ struct bpf_diag *diag = env->diag; return diag ? log_end(&diag->log) : 0;} void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos){ struct bpf_diag *diag = env->diag; struct bpf_diag_log *log; u64 end_seq; if (!diag) return; log = &diag->log; end_seq = log_end(log); if (WARN_ON_ONCE(log_pos > end_seq)) log_pos = end_seq; /* * A deep abandoned path may have rotated away the shared prefix. In * that case, restart with an empty retained suffix and remember that * every event before the restored mark is unavailable. */ if (log_pos <= log->first_seq) { log->first_seq = log_pos; log->head = 0; log->cnt = 0; return; } log->cnt = log_pos - log->first_seq;} u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state){ u32 depth = 0; int i; for (i = 0; i < state->acquired_refs; i++) { if (state->refs[i].type == REF_TYPE_IRQ) depth++; } return depth;} static void diag_append_history(struct bpf_verifier_env *env, const struct bpf_diag_history_event *event){ struct bpf_diag_history_event *events; struct bpf_diag *diag = env->diag; struct bpf_diag_log *log; u32 cap, max_events; if (!diag) return; log = &diag->log; if (log->cnt < log->cap) { log->events[log_pos(log, log->cnt++)] = *event; return; } max_events = BPF_DIAG_EVENT_LOG_MAX_SIZE / sizeof(*events); if (log->growth_failed || log->cap == max_events) goto rotate; cap = min(log->cap ? log->cap * 2 : 64, max_events); events = kvrealloc(log->events, array_size(cap, sizeof(*events)), GFP_KERNEL_ACCOUNT); if (!events) { log->growth_failed = true; goto rotate; } log->events = events; log->cap = cap; log->events[log->cnt++] = *event; return; rotate: if (log->cap) { log->events[log->head++] = *event; if (log->head == log->cap) log->head = 0; } log->first_seq++;} 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] == ' ')Lines 1–500 of 2359. Load more
// SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2026 Meta Platforms, Inc. and affiliates. #include <linux/bpf.h> #include <linux/bpf_verifier.h> #include <linux/btf.h> #include <linux/ctype.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/seq_buf.h> #include <linux/overflow.h> #include <linux/slab.h> #include <linux/stdarg.h> #include <linux/string.h> #include "disasm.h" #include "diagnostics.h" #define REGISTER_TYPE_SAFETY "Register Type Safety" #define MEMORY_SAFETY "Memory Safety" #define RESOURCE_LIFETIME_SAFETY "Resource Lifetime Safety" #define CALL_TYPE_SAFETY "Call Type Safety" #define EXECUTION_CONTEXT_SAFETY "Execution Context Safety" #define PROGRAM_STRUCTURE "Program Structure" #define POLICY "Policy" #define BPF_DIAG_TEXT_WIDTH 100 #define BPF_DIAG_TEXT_INDENT " " #define BPF_DIAG_CONTEXT 2 #define BPF_DIAG_CONTEXT_CNT (1 + BPF_DIAG_CONTEXT * 2) #define BPF_DIAG_HISTORY_RENDER_MAX 64 #define BPF_DIAG_SOURCE_LANE_WIDTH 88 #define BPF_DIAG_TAB_WIDTH 8 #define BPF_DIAG_FMT_CHUNK_SIZE (PAGE_SIZE - sizeof(struct diag_fmt_chunk)) #define BPF_DIAG_FMT_BUF_SIZE 256 #define BPF_DIAG_EVENT_LOG_MAX_SIZE (64U << 20) #define DISASM_LINE_LEN 160 enum bpf_diag_mod_target_kind { BPF_DIAG_MOD_TARGET_NONE, BPF_DIAG_MOD_TARGET_REG, BPF_DIAG_MOD_TARGET_STACK_ARG, BPF_DIAG_MOD_TARGET_STACK_SLOT, BPF_DIAG_MOD_TARGET_STACK_RANGE, }; struct bpf_diag_mod_target { u32 frame_id; union { struct { s16 min_off; s16 max_off; } range; u16 spi; u8 regno; u8 stack_arg; }; u8 frameno; u8 kind; }; static struct bpf_diag_mod_target diag_reg_target(u32 frame_id, u8 frameno, u8 regno) { return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_REG, .regno = regno, }; } static struct bpf_diag_mod_target diag_stack_arg_target(u32 frame_id, u8 frameno, u8 slot) { return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_STACK_ARG, .stack_arg = slot, }; } static struct bpf_diag_mod_target diag_stack_slot_target(u32 frame_id, u8 frameno, u16 spi) { return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_STACK_SLOT, .spi = spi, }; } static struct bpf_diag_mod_target diag_stack_range_target(u32 frame_id, u8 frameno, s16 min_off, s16 max_off) { return (struct bpf_diag_mod_target){ .frame_id = frame_id, .frameno = frameno, .kind = BPF_DIAG_MOD_TARGET_STACK_RANGE, .range.min_off = min_off, .range.max_off = max_off, }; } struct bpf_diag_reg_snapshot { u32 type; u32 btf_id; const struct bpf_map *map_ptr; const struct btf *btf; struct tnum var_off; struct cnum64 r64; }; enum bpf_diag_history_kind { BPF_DIAG_HISTORY_BRANCH, BPF_DIAG_HISTORY_MOD, BPF_DIAG_HISTORY_REF_ACQUIRE, BPF_DIAG_HISTORY_REF_RELEASE, BPF_DIAG_HISTORY_CONTEXT, }; struct bpf_diag_history_event { u32 insn_idx : 24; u32 kind : 8; u8 in_lineage : 1; union { struct { bool cond_true; } branch; struct { struct bpf_diag_mod_target target; struct bpf_diag_mod_target origin; struct bpf_diag_reg_snapshot old, new; u8 reason; bool origin_valid; } mod; struct { u32 ref_id; } ref; struct { u32 depth; u8 kind; bool enter; } ctx; }; }; enum bpf_diag_history_scope { BPF_DIAG_HISTORY_SCOPE_REG, BPF_DIAG_HISTORY_SCOPE_STACK_ARG, BPF_DIAG_HISTORY_SCOPE_REF, BPF_DIAG_HISTORY_SCOPE_CONTEXT, }; struct bpf_diag_history_opts { enum bpf_diag_history_scope scope; u32 frame_id; u32 frameno; int regno; int stack_arg_slot; u32 ref_id; enum bpf_diag_context_kind ctx_kind; u32 ctx_depth; }; static void diag_print_history(struct bpf_verifier_env *env, const struct bpf_diag_history_opts *opts); static bool diag_target_matches(const struct bpf_diag_mod_target *event_target, const struct bpf_diag_mod_target *target); static const char *diag_context_name(enum bpf_diag_context_kind kind); struct disasm_line { char text[DISASM_LINE_LEN]; int idx; bool valid; }; struct disasm_ctx { struct bpf_verifier_env *env; struct seq_buf seq; }; struct diag_fmt_chunk { struct list_head node; struct seq_buf seq; char data[]; }; struct diag_fmt_mark { struct diag_fmt_chunk *chunk; size_t len; }; struct bpf_diag_log { struct bpf_diag_history_event *events; /* Sequence number of the oldest retained event on the active path. */ u64 first_seq; u32 cnt; u32 cap; u32 head; bool growth_failed; }; struct bpf_diag_scratch { struct bpf_linfo_source source_lines[BPF_DIAG_CONTEXT_CNT]; struct disasm_line disasm_lines[BPF_DIAG_CONTEXT_CNT]; }; struct bpf_diag_mod_scope { struct bpf_reg_state target_reg_snapshot; struct bpf_diag_mod_target target; struct bpf_diag_mod_target origin; enum bpf_diag_mod_reason reason; u32 insn_idx; bool active; bool origin_valid; }; struct bpf_diag { struct bpf_diag_log log; struct bpf_diag_scratch scratch; struct list_head fmt_chunks; struct bpf_diag_mod_scope mod; u32 frame_id_gen; }; bool bpf_diag_enabled(const struct bpf_verifier_env *env) { return env->log.level & BPF_LOG_LEVEL; } static void diag_write(struct bpf_verifier_env *env, const char *fmt, ...) __printf(2, 3); int bpf_diag_init(struct bpf_verifier_env *env) { if (!bpf_diag_enabled(env)) return 0; env->diag = kzalloc_obj(struct bpf_diag, GFP_KERNEL_ACCOUNT); if (!env->diag) return -ENOMEM; INIT_LIST_HEAD(&env->diag->fmt_chunks); return 0; } void bpf_diag_init_frame(struct bpf_verifier_env *env, struct bpf_func_state *state) { if (env->diag) state->diag_frame_id = ++env->diag->frame_id_gen; } static char *diag_fmt_alloc(struct bpf_verifier_env *env, size_t size) { struct bpf_diag *diag = env->diag; struct diag_fmt_chunk *chunk; size_t capacity, available; char *buf; if (!diag || !size || size > INT_MAX) return NULL; if (!list_empty(&diag->fmt_chunks)) { chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); available = seq_buf_get_buf(&chunk->seq, &buf); if (available >= size) goto commit; } capacity = max_t(size_t, BPF_DIAG_FMT_CHUNK_SIZE, size); chunk = kmalloc_flex(*chunk, data, capacity, GFP_KERNEL_ACCOUNT); if (!chunk) return NULL; seq_buf_init(&chunk->seq, chunk->data, capacity); list_add_tail(&chunk->node, &diag->fmt_chunks); available = seq_buf_get_buf(&chunk->seq, &buf); if (WARN_ON_ONCE(available < size)) return NULL; commit: seq_buf_commit(&chunk->seq, size); return buf; } char *bpf_diag_fmt_buf(struct bpf_verifier_env *env, size_t size) { char *buf; buf = diag_fmt_alloc(env, size); if (buf) buf[0] = '\0'; return buf; } const char *bpf_diag_vfmt(struct bpf_verifier_env *env, const char *fmt, va_list args) { va_list copy; char *buf; int len; va_copy(copy, args); len = vsnprintf(NULL, 0, fmt, copy); va_end(copy); if (len < 0 || len == INT_MAX) return ""; buf = diag_fmt_alloc(env, len + 1); if (buf) vsnprintf(buf, len + 1, fmt, args); return buf ?: ""; } const char *bpf_diag_fmt(struct bpf_verifier_env *env, const char *fmt, ...) { const char *buf; va_list args; va_start(args, fmt); buf = bpf_diag_vfmt(env, fmt, args); va_end(args); return buf; } static struct diag_fmt_mark diag_fmt_save(struct bpf_verifier_env *env) { struct bpf_diag *diag = env->diag; struct diag_fmt_mark mark = {}; if (!diag || list_empty(&diag->fmt_chunks)) return mark; mark.chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); mark.len = mark.chunk->seq.len; return mark; } static void diag_fmt_restore(struct bpf_verifier_env *env, struct diag_fmt_mark mark) { struct bpf_diag *diag = env->diag; struct diag_fmt_chunk *chunk; if (!diag) return; while (!list_empty(&diag->fmt_chunks)) { chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); if (chunk == mark.chunk) break; list_del(&chunk->node); kfree(chunk); } if (mark.chunk) { mark.chunk->seq.len = mark.len; seq_buf_str(&mark.chunk->seq); } } void bpf_diag_free(struct bpf_verifier_env *env) { struct bpf_diag *diag = env->diag; if (!diag) return; diag_fmt_restore(env, (struct diag_fmt_mark){}); kvfree(diag->log.events); kfree(diag); env->diag = NULL; } static void diag_write(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 u64 log_end(const struct bpf_diag_log *log) { return log->first_seq + log->cnt; } static u32 log_pos(const struct bpf_diag_log *log, u32 idx) { u32 pos = log->head + idx; return pos < log->cap ? pos : pos - log->cap; } u64 bpf_diag_event_log_save(struct bpf_verifier_env *env) { struct bpf_diag *diag = env->diag; return diag ? log_end(&diag->log) : 0; } void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos) { struct bpf_diag *diag = env->diag; struct bpf_diag_log *log; u64 end_seq; if (!diag) return; log = &diag->log; end_seq = log_end(log); if (WARN_ON_ONCE(log_pos > end_seq)) log_pos = end_seq; /* * A deep abandoned path may have rotated away the shared prefix. In * that case, restart with an empty retained suffix and remember that * every event before the restored mark is unavailable. */ if (log_pos <= log->first_seq) { log->first_seq = log_pos; log->head = 0; log->cnt = 0; return; } log->cnt = log_pos - log->first_seq; } u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state) { u32 depth = 0; int i; for (i = 0; i < state->acquired_refs; i++) { if (state->refs[i].type == REF_TYPE_IRQ) depth++; } return depth; } static void diag_append_history(struct bpf_verifier_env *env, const struct bpf_diag_history_event *event) { struct bpf_diag_history_event *events; struct bpf_diag *diag = env->diag; struct bpf_diag_log *log; u32 cap, max_events; if (!diag) return; log = &diag->log; if (log->cnt < log->cap) { log->events[log_pos(log, log->cnt++)] = *event; return; } max_events = BPF_DIAG_EVENT_LOG_MAX_SIZE / sizeof(*events); if (log->growth_failed || log->cap == max_events) goto rotate; cap = min(log->cap ? log->cap * 2 : 64, max_events); events = kvrealloc(log->events, array_size(cap, sizeof(*events)), GFP_KERNEL_ACCOUNT); if (!events) { log->growth_failed = true; goto rotate; } log->events = events; log->cap = cap; log->events[log->cnt++] = *event; return; rotate: if (log->cap) { log->events[log->head++] = *event; if (log->head == log->cap) log->head = 0; } log->first_seq++; } 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] == ' ')