1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright (c) 2026 Meta Platforms, Inc. and affiliates. 3 4 #include <linux/bpf.h> 5 #include <linux/bpf_verifier.h> 6 #include <linux/btf.h> 7 #include <linux/ctype.h> 8 #include <linux/kernel.h> 9 #include <linux/list.h> 10 #include <linux/seq_buf.h> 11 #include <linux/slab.h> 12 #include <linux/stdarg.h> 13 #include <linux/string.h> 14 15 #include "disasm.h" 16 #include "diagnostics.h" 17 18 #define BPF_DIAG_TEXT_WIDTH 100 19 #define BPF_DIAG_CONTEXT 2 20 #define BPF_DIAG_CONTEXT_CNT (1 + BPF_DIAG_CONTEXT * 2) 21 #define BPF_DIAG_SOURCE_LANE_WIDTH 88 22 #define BPF_DIAG_TAB_WIDTH 8 23 #define BPF_DIAG_FMT_CHUNK_SIZE (PAGE_SIZE - sizeof(struct diag_fmt_chunk)) 24 #define BPF_DIAG_FMT_BUF_SIZE 256 25 #define BPF_DIAG_EVENT_LOG_MAX_SIZE (64U << 20) 26 #define DISASM_LINE_LEN 160 27 28 enum bpf_diag_history_kind { 29 BPF_DIAG_HISTORY_BRANCH, 30 }; 31 32 struct bpf_diag_history_event { 33 u32 insn_idx : 24; 34 u32 kind : 8; 35 u8 in_lineage : 1; 36 union { 37 struct { 38 bool cond_true; 39 } branch; 40 }; 41 }; 42 43 struct disasm_line { 44 char text[DISASM_LINE_LEN]; 45 int idx; 46 bool valid; 47 }; 48 49 struct disasm_ctx { 50 struct bpf_verifier_env *env; 51 struct seq_buf seq; 52 }; 53 54 struct diag_fmt_chunk { 55 struct list_head node; 56 struct seq_buf seq; 57 char data[]; 58 }; 59 60 struct diag_fmt_mark { 61 struct diag_fmt_chunk *chunk; 62 size_t len; 63 }; 64 65 struct bpf_diag_log { 66 struct bpf_diag_history_event *events; 67 /* Sequence number of the oldest retained event on the active path. */ 68 u64 first_seq; 69 u32 cnt; 70 u32 cap; 71 u32 head; 72 bool growth_failed; 73 }; 74 75 struct bpf_diag_scratch { 76 struct bpf_linfo_source source_lines[BPF_DIAG_CONTEXT_CNT]; 77 struct disasm_line disasm_lines[BPF_DIAG_CONTEXT_CNT]; 78 }; 79 80 struct bpf_diag { 81 struct bpf_diag_log log; 82 struct bpf_diag_scratch scratch; 83 struct list_head fmt_chunks; 84 }; 85 86 bool bpf_diag_enabled(const struct bpf_verifier_env *env) 87 { 88 return env->log.level & BPF_LOG_LEVEL; 89 } 90 91 static void diag_write(struct bpf_verifier_env *env, const char *fmt, ...) __printf(2, 3); 92 93 int bpf_diag_init(struct bpf_verifier_env *env) 94 { 95 if (!bpf_diag_enabled(env)) 96 return 0; 97 98 env->diag = kzalloc_obj(struct bpf_diag, GFP_KERNEL_ACCOUNT); 99 if (!env->diag) 100 return -ENOMEM; 101 102 INIT_LIST_HEAD(&env->diag->fmt_chunks); 103 return 0; 104 } 105 106 static char *diag_fmt_alloc(struct bpf_verifier_env *env, size_t size) 107 { 108 struct bpf_diag *diag = env->diag; 109 struct diag_fmt_chunk *chunk; 110 size_t capacity, available; 111 char *buf; 112 113 if (!diag || !size || size > INT_MAX) 114 return NULL; 115 116 if (!list_empty(&diag->fmt_chunks)) { 117 chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); 118 available = seq_buf_get_buf(&chunk->seq, &buf); 119 if (available >= size) 120 goto commit; 121 } 122 123 capacity = max_t(size_t, BPF_DIAG_FMT_CHUNK_SIZE, size); 124 chunk = kmalloc(struct_size(chunk, data, capacity), GFP_KERNEL_ACCOUNT); 125 if (!chunk) 126 return NULL; 127 128 seq_buf_init(&chunk->seq, chunk->data, capacity); 129 list_add_tail(&chunk->node, &diag->fmt_chunks); 130 available = seq_buf_get_buf(&chunk->seq, &buf); 131 if (WARN_ON_ONCE(available < size)) 132 return NULL; 133 134 commit: 135 seq_buf_commit(&chunk->seq, size); 136 return buf; 137 } 138 139 char *bpf_diag_fmt_buf(struct bpf_verifier_env *env, size_t size) 140 { 141 char *buf; 142 143 buf = diag_fmt_alloc(env, size); 144 if (buf) 145 buf[0] = '\0'; 146 return buf; 147 } 148 149 const char *bpf_diag_vfmt(struct bpf_verifier_env *env, const char *fmt, va_list args) 150 { 151 va_list copy; 152 char *buf; 153 int len; 154 155 va_copy(copy, args); 156 len = vsnprintf(NULL, 0, fmt, copy); 157 va_end(copy); 158 if (len < 0 || len == INT_MAX) 159 return ""; 160 161 buf = diag_fmt_alloc(env, len + 1); 162 if (buf) 163 vsnprintf(buf, len + 1, fmt, args); 164 return buf ?: ""; 165 } 166 167 const char *bpf_diag_fmt(struct bpf_verifier_env *env, const char *fmt, ...) 168 { 169 const char *buf; 170 va_list args; 171 172 va_start(args, fmt); 173 buf = bpf_diag_vfmt(env, fmt, args); 174 va_end(args); 175 return buf; 176 } 177 178 static struct diag_fmt_mark diag_fmt_save(struct bpf_verifier_env *env) 179 { 180 struct bpf_diag *diag = env->diag; 181 struct diag_fmt_mark mark = {}; 182 183 if (!diag || list_empty(&diag->fmt_chunks)) 184 return mark; 185 186 mark.chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); 187 mark.len = mark.chunk->seq.len; 188 return mark; 189 } 190 191 static void diag_fmt_restore(struct bpf_verifier_env *env, struct diag_fmt_mark mark) 192 { 193 struct bpf_diag *diag = env->diag; 194 struct diag_fmt_chunk *chunk; 195 196 if (!diag) 197 return; 198 199 while (!list_empty(&diag->fmt_chunks)) { 200 chunk = list_last_entry(&diag->fmt_chunks, struct diag_fmt_chunk, node); 201 if (chunk == mark.chunk) 202 break; 203 list_del(&chunk->node); 204 kfree(chunk); 205 } 206 207 if (mark.chunk) { 208 mark.chunk->seq.len = mark.len; 209 seq_buf_str(&mark.chunk->seq); 210 } 211 } 212 213 void bpf_diag_free(struct bpf_verifier_env *env) 214 { 215 struct bpf_diag *diag = env->diag; 216 217 if (!diag) 218 return; 219 220 diag_fmt_restore(env, (struct diag_fmt_mark){}); 221 kvfree(diag->log.events); 222 kfree(diag); 223 env->diag = NULL; 224 } 225 226 static void diag_write(struct bpf_verifier_env *env, const char *fmt, ...) 227 { 228 va_list args; 229 230 if (!bpf_diag_enabled(env)) 231 return; 232 233 va_start(args, fmt); 234 bpf_verifier_vlog(&env->log, fmt, args); 235 va_end(args); 236 } 237 238 static u64 log_end(const struct bpf_diag_log *log) 239 { 240 return log->first_seq + log->cnt; 241 } 242 243 static u32 log_pos(const struct bpf_diag_log *log, u32 idx) 244 { 245 u32 pos = log->head + idx; 246 247 return pos < log->cap ? pos : pos - log->cap; 248 } 249 250 u64 bpf_diag_event_log_save(struct bpf_verifier_env *env) 251 { 252 struct bpf_diag *diag = env->diag; 253 254 return diag ? log_end(&diag->log) : 0; 255 } 256 257 void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos) 258 { 259 struct bpf_diag *diag = env->diag; 260 struct bpf_diag_log *log; 261 u64 end_seq; 262 263 if (!diag) 264 return; 265 266 log = &diag->log; 267 end_seq = log_end(log); 268 if (WARN_ON_ONCE(log_pos > end_seq)) 269 log_pos = end_seq; 270 271 /* 272 * A deep abandoned path may have rotated away the shared prefix. In 273 * that case, restart with an empty retained suffix and remember that 274 * every event before the restored mark is unavailable. 275 */ 276 if (log_pos <= log->first_seq) { 277 log->first_seq = log_pos; 278 log->head = 0; 279 log->cnt = 0; 280 return; 281 } 282 283 log->cnt = log_pos - log->first_seq; 284 } 285 286 static void diag_append_history(struct bpf_verifier_env *env, 287 const struct bpf_diag_history_event *event) 288 { 289 struct bpf_diag_history_event *events; 290 struct bpf_diag *diag = env->diag; 291 struct bpf_diag_log *log; 292 u32 cap, max_events; 293 294 if (!diag) 295 return; 296 log = &diag->log; 297 298 if (log->cnt < log->cap) { 299 log->events[log_pos(log, log->cnt++)] = *event; 300 return; 301 } 302 303 max_events = BPF_DIAG_EVENT_LOG_MAX_SIZE / sizeof(*events); 304 if (log->growth_failed || log->cap == max_events) 305 goto rotate; 306 307 cap = min(log->cap ? log->cap * 2 : 64, max_events); 308 events = kvrealloc(log->events, array_size(cap, sizeof(*events)), GFP_KERNEL_ACCOUNT); 309 if (!events) { 310 log->growth_failed = true; 311 goto rotate; 312 } 313 log->events = events; 314 log->cap = cap; 315 log->events[log->cnt++] = *event; 316 return; 317 318 rotate: 319 if (log->cap) { 320 log->events[log->head++] = *event; 321 if (log->head == log->cap) 322 log->head = 0; 323 } 324 log->first_seq++; 325 } 326 327 static void diag_print_wrapped_prefixed(struct bpf_verifier_env *env, const char *first_prefix, 328 const char *next_prefix, const char *text) 329 { 330 const char *prefix = first_prefix; 331 332 while (*text) { 333 const char *line = text; 334 int prefix_len = strlen(prefix); 335 int text_width = BPF_DIAG_TEXT_WIDTH - prefix_len; 336 int len = 0, last_space = -1; 337 338 if (text_width < 1) 339 text_width = 1; 340 341 while (line[len] && line[len] != '\n' && len < text_width) { 342 if (line[len] == ' ') 343 last_space = len; 344 len++; 345 } 346 347 if (line[len] && line[len] != '\n' && line[len] != ' ' && last_space > 0) 348 len = last_space; 349 350 diag_write(env, "%s%.*s\n", prefix, len, line); 351 352 text = line + len; 353 while (*text == ' ') 354 text++; 355 if (*text == '\n') 356 text++; 357 358 prefix = next_prefix; 359 } 360 } 361 362 static int diag_line_width(unsigned int line) 363 { 364 int width = 1; 365 366 while (line >= 10) { 367 line /= 10; 368 width++; 369 } 370 371 return width; 372 } 373 374 static int diag_line_indent(const char *line) 375 { 376 int indent = 0; 377 378 while (*line == ' ' || *line == '\t') { 379 if (*line == '\t') 380 indent = round_up(indent + 1, BPF_DIAG_TAB_WIDTH); 381 else 382 indent++; 383 line++; 384 } 385 386 return indent; 387 } 388 389 static void disasm_print(void *private_data, const char *fmt, ...) __printf(2, 3); 390 391 static void disasm_print(void *private_data, const char *fmt, ...) 392 { 393 struct disasm_ctx *ctx = private_data; 394 va_list args; 395 396 va_start(args, fmt); 397 seq_buf_vprintf(&ctx->seq, fmt, args); 398 va_end(args); 399 } 400 401 static const char *disasm_kfunc_name(void *private_data, const struct bpf_insn *insn) 402 { 403 struct disasm_ctx *ctx = private_data; 404 405 return bpf_disasm_kfunc_name(ctx->env, insn); 406 } 407 408 static void format_disasm_line(struct bpf_verifier_env *env, int insn_idx, 409 struct disasm_line *line) 410 { 411 struct disasm_ctx ctx = { .env = env }; 412 struct bpf_insn *insn; 413 const struct bpf_insn_cbs cbs = { 414 .cb_call = disasm_kfunc_name, 415 .cb_print = disasm_print, 416 .private_data = &ctx, 417 }; 418 419 line->idx = insn_idx; 420 line->valid = false; 421 seq_buf_init(&ctx.seq, line->text, sizeof(line->text)); 422 423 if (insn_idx < 0 || insn_idx >= env->prog->len) 424 return; 425 426 if (insn_idx > 0 && bpf_is_ldimm64(&env->prog->insnsi[insn_idx - 1])) 427 return; 428 429 insn = &env->prog->insnsi[insn_idx]; 430 if (bpf_is_ldimm64(insn) && insn_idx + 1 >= env->prog->len) 431 return; 432 433 print_bpf_insn(&cbs, insn, env->allow_ptr_leaks); 434 seq_buf_str(&ctx.seq); 435 ctx.seq.len = strnlen(line->text, sizeof(line->text)); 436 while (ctx.seq.len && line->text[ctx.seq.len - 1] == '\n') 437 seq_buf_pop(&ctx.seq); 438 seq_buf_str(&ctx.seq); 439 440 line->valid = true; 441 } 442 443 static void diag_format_source_text(char *buf, size_t size, const char *line, int width) 444 { 445 int col = 0, len = 0; 446 447 if (!size) 448 return; 449 if (width <= 0) { 450 buf[0] = '\0'; 451 return; 452 } 453 454 line = line ?: "..."; 455 while (*line && col < width && len + 1 < size) { 456 if (*line == '\t') { 457 int next = round_up(col + 1, BPF_DIAG_TAB_WIDTH); 458 459 while (col < next && col < width && len + 1 < size) { 460 buf[len++] = ' '; 461 col++; 462 } 463 line++; 464 continue; 465 } 466 467 buf[len++] = *line++; 468 col++; 469 } 470 471 if (*line) { 472 int ellipsis_len = min(3, width); 473 474 while (len > 0 && col > width - ellipsis_len) { 475 len--; 476 col--; 477 } 478 while (ellipsis_len-- && len + 1 < size) 479 buf[len++] = '.'; 480 } 481 482 buf[len] = '\0'; 483 } 484 485 static void diag_format_source_lane(char *buf, size_t size, const char *source_prefix, 486 int source_line_width, int line_num, const char *line) 487 { 488 int len, text_width; 489 490 if (line_num <= 0) { 491 buf[0] = '\0'; 492 return; 493 } 494 495 len = scnprintf(buf, size, "%s%*d | ", source_prefix, source_line_width, line_num); 496 text_width = BPF_DIAG_SOURCE_LANE_WIDTH - len; 497 diag_format_source_text(buf + len, size - len, line, text_width); 498 } 499 500 static void bpf_diag_header(struct bpf_verifier_env *env, const char *category, 501 const char *problem) 502 { 503 char first; 504 505 if (!bpf_diag_enabled(env)) 506 return; 507 508 category = category ?: "Verifier Error"; 509 problem = problem ?: ""; 510 511 if (!problem[0]) { 512 diag_write(env, "\nVerification failed: %s\n", category); 513 return; 514 } 515 516 first = toupper(problem[0]); 517 diag_write(env, "\nVerification failed: %s: %c%s\n", category, first, problem + 1); 518 } 519 520 static void diag_print_source_annotation(struct bpf_verifier_env *env, int line_width, int indent, 521 const char *label, const char *msg) 522 { 523 const char *first_prefix, *next_prefix, *text; 524 525 indent = min_t(int, indent, max_t(int, 0, BPF_DIAG_SOURCE_LANE_WIDTH - line_width - 8)); 526 text = bpf_diag_fmt(env, "%s: %s", label, msg); 527 first_prefix = bpf_diag_fmt(env, " %*s | %*s^-- ", line_width + 4, "", indent, ""); 528 next_prefix = bpf_diag_fmt(env, " %*s | %*s ", line_width + 4, "", indent, ""); 529 530 diag_print_wrapped_prefixed(env, first_prefix, next_prefix, text); 531 } 532 533 static void diag_print_insn_context(struct bpf_verifier_env *env, u32 insn_idx, 534 struct disasm_line *disasm_lines) 535 { 536 int insn_width = diag_line_width(env->prog->len ? env->prog->len - 1 : 0); 537 int i; 538 539 for (i = 0; i < BPF_DIAG_CONTEXT_CNT; i++) { 540 int row = i - BPF_DIAG_CONTEXT; 541 542 format_disasm_line(env, insn_idx + row, &disasm_lines[i]); 543 } 544 545 diag_write(env, " Instruction context:\n"); 546 for (i = 0; i < BPF_DIAG_CONTEXT_CNT; i++) { 547 struct disasm_line *line = &disasm_lines[i]; 548 549 if (line->valid) 550 diag_write(env, " %s%*d | %s\n", 551 line->idx == insn_idx ? ">>> " : " ", 552 insn_width, line->idx, line->text); 553 } 554 } 555 556 static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const char *label, 557 const char *fmt, ...) 558 { 559 struct bpf_diag_scratch *scratch; 560 struct bpf_linfo_source *source_lines; 561 struct disasm_line *disasm_lines; 562 struct bpf_linfo_source src = {}; 563 struct diag_fmt_mark mark; 564 const struct bpf_line_info *linfo; 565 const struct bpf_subprog_info *subprog; 566 struct btf *btf = env->prog->aux->btf; 567 char *source_lane; 568 const char *msg; 569 const char *func; 570 int start_line, end_line, width, indent, subprogno, linfo_start, linfo_end, i; 571 va_list args; 572 573 if (!bpf_diag_enabled(env)) 574 return; 575 if (!env->diag) 576 return; 577 578 mark = diag_fmt_save(env); 579 label = label ?: "note"; 580 scratch = &env->diag->scratch; 581 source_lines = scratch->source_lines; 582 disasm_lines = scratch->disasm_lines; 583 memset(source_lines, 0, sizeof(scratch->source_lines)); 584 memset(disasm_lines, 0, sizeof(scratch->disasm_lines)); 585 586 va_start(args, fmt); 587 msg = bpf_diag_vfmt(env, fmt, args); 588 va_end(args); 589 if (!*msg) 590 msg = "<failed to allocate diagnostic text>"; 591 592 linfo = bpf_find_linfo(env->prog, insn_idx); 593 if (btf && linfo) 594 bpf_get_linfo_source(btf, linfo, &src); 595 if (!src.file || !*src.file || !src.line || !*src.line) { 596 diag_write(env, " insn %u\n", insn_idx); 597 diag_print_source_annotation(env, 0, 0, label, msg); 598 diag_print_insn_context(env, insn_idx, disasm_lines); 599 goto out_restore; 600 } 601 602 subprog = bpf_find_containing_subprog(env, insn_idx); 603 subprogno = subprog ? subprog - env->subprog_info : -ENOENT; 604 func = subprogno >= 0 ? bpf_subprog_name(env, subprogno) : NULL; 605 if (func && *func) 606 diag_write(env, " %s @ %s:%d:%d\n", func, src.file, src.line_num, src.line_col); 607 else 608 diag_write(env, " %s:%d:%d\n", src.file, src.line_num, src.line_col); 609 610 start_line = src.line_num - BPF_DIAG_CONTEXT; 611 end_line = src.line_num + BPF_DIAG_CONTEXT; 612 width = diag_line_width(end_line); 613 indent = diag_line_indent(src.line); 614 for (i = 0; i < BPF_DIAG_CONTEXT_CNT; i++) 615 source_lines[i].line_num = start_line + i; 616 617 linfo = env->prog->aux->linfo; 618 linfo_start = subprog ? subprog->linfo_idx : 0; 619 linfo_end = subprogno >= 0 && subprogno + 1 < env->subprog_cnt ? 620 env->subprog_info[subprogno + 1].linfo_idx : env->prog->aux->nr_linfo; 621 for (i = linfo_start; i < linfo_end; i++) { 622 struct bpf_linfo_source line_src; 623 int idx; 624 625 bpf_get_linfo_source(btf, &linfo[i], &line_src); 626 if (line_src.file_name_off != src.file_name_off || 627 line_src.line_num < start_line || line_src.line_num > end_line || 628 !line_src.line || !*line_src.line) 629 continue; 630 631 idx = line_src.line_num - start_line; 632 if (!source_lines[idx].line) 633 source_lines[idx] = line_src; 634 } 635 636 diag_write(env, " Source context:\n"); 637 source_lane = bpf_diag_fmt_buf(env, BPF_DIAG_FMT_BUF_SIZE); 638 if (!source_lane) 639 goto out_restore; 640 for (i = 0; i < BPF_DIAG_CONTEXT_CNT; i++) { 641 const char *source_prefix; 642 643 source_prefix = source_lines[i].line_num == src.line_num ? ">>> " : " "; 644 diag_format_source_lane(source_lane, BPF_DIAG_FMT_BUF_SIZE, source_prefix, width, 645 source_lines[i].line_num, source_lines[i].line); 646 diag_write(env, " %s\n", source_lane); 647 if (source_lines[i].line_num == src.line_num) 648 diag_print_source_annotation(env, width, indent, label, msg); 649 } 650 diag_print_insn_context(env, insn_idx, disasm_lines); 651 652 out_restore: 653 diag_fmt_restore(env, mark); 654 } 655 656 void bpf_diag_record_branch(struct bpf_verifier_env *env, u32 insn_idx, bool cond_true) 657 { 658 struct bpf_diag_history_event event = { 659 .insn_idx = insn_idx, 660 .kind = BPF_DIAG_HISTORY_BRANCH, 661 .branch = { 662 .cond_true = cond_true, 663 }, 664 }; 665 666 diag_append_history(env, &event); 667 } 668