fix(sqlite_writer): skip index cells that exceed a single page

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.
This commit is contained in:
Jos Joosten
2026-05-10 19:57:28 +02:00
committed by DeusData
parent 5f19454724
commit 98d5cacd76
+10 -3
View File
@@ -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]);