From 4972589877843db89308985bbc670446c445a2be Mon Sep 17 00:00:00 2001 From: Jos Joosten Date: Wed, 22 Apr 2026 14:22:39 +0200 Subject: [PATCH] fix(sqlite_writer): skip index cells that exceed a single page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit write_index_btree() flushed the page buffer whenever the next cell didn't fit, on the assumption that a freshly-initialised page would always accept any cell. That invariant fails for index cells whose payload (qualified_name, file_path, etc.) is larger than a full SQLite page — after the flush, pb_cell_fits() is still false but the code calls pb_add_cell() anyway. The subsequent content_offset underflow corrupts the page header and the next write triggers SIGBUS on large repos (observed on a Ruby codebase with ~215K nodes). Re-check pb_cell_fits() after the flush and continue past cells that still don't fit. Index entries whose keys exceed a full page can't be stored and are not expected to survive the writer anyway — the rest of the index is correctly preserved and the indexer no longer crashes. Record overflow pages (#175) already covers the record-btree side of this same shape of bug. --- internal/cbm/sqlite_writer.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/cbm/sqlite_writer.c b/internal/cbm/sqlite_writer.c index f7b4a008..b8afd5bc 100644 --- a/internal/cbm/sqlite_writer.c +++ b/internal/cbm/sqlite_writer.c @@ -1213,9 +1213,16 @@ static uint32_t write_index_btree(FILE *fp, uint32_t *next_page, uint8_t **cells pb_init(&pb, fp, *next_page, true); for (int i = 0; i < count; i++) { - if (!pb_cell_fits(&pb, cell_lens[i]) && pb.cell_count > 0) { - if (!pb_promote_and_flush(&pb, cells, cell_lens, i - SKIP_ONE)) { - return 0; + if (!pb_cell_fits(&pb, cell_lens[i])) { + if (pb.cell_count > 0) { + if (!pb_promote_and_flush(&pb, cells, cell_lens, i - SKIP_ONE)) { + return 0; + } + } + // After flush, check if the cell still doesn't fit on an empty page. + // Index cells larger than a full page can never be stored; skip them. + if (!pb_cell_fits(&pb, cell_lens[i])) { + continue; } } pb_add_cell(&pb, cells[i], cell_lens[i]);