Skip to content

test(faultinject): deepen malloc-failure sweep (M 506->947) — 1 new crash + 2 new OOM leaks - #90

Merged
gburd merged 2 commits into
masterfrom
agent/malloc-deepen
Jul 30, 2026
Merged

test(faultinject): deepen malloc-failure sweep (M 506->947) — 1 new crash + 2 new OOM leaks#90
gburd merged 2 commits into
masterfrom
agent/malloc-deepen

Conversation

@gburd

@gburd gburd commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

What

Broadens the SQLite-style malloc-failure injection sweep (test/faultinject/fi_sweep.c, from PR #49) so its workload reaches substantially more allocation sites, then re-runs the sweep under plain-debug and clang ASan.

The old workload (open env + btree + hash, put/get/cursor/txn commit+abort, checkpoint) reached M = 506 alloc sites. It now also drives the warmer error paths functional tests miss:

  • secondary index — associate + get-by-secondary + pget
  • join cursor — DB->join
  • bulk put/get — DB_MULTIPLE_KEY write buffer + DB_MULTIPLE_KEY cursor scan
  • in-memory DB (NULL filename)
  • subdatabase open (named DB in a container file)
  • compaction — DB->compact with DB_FREE_SPACE
  • DB->stat
  • 2PC — txn->prepare then resolve

New baseline: M = 947 (+441 sites, ~87% more failure points swept).

txn_recover was intentionally left out: on a still-live in-process prepared txn it double-resolves and panics the region (DB_RUNRECOVERY). It is a separate-process recovery op; prepare alone reaches the new 2PC alloc sites.

Results (deterministic, K = 1..947)

Metric old (M=506) new (M=947)
Failure points exercised 506 947
Clean error return 467 861
Clean/tolerated 31 81
CRASH (4 root causes, since fixed) 5
HANG 0 0
DIRTY 0 0

Plain-debug sweep: ~46s. ASan full one-shot sweep: ~2min. The four original bugs (PR #52) no longer reproduce on the current tree.

New OOM bugs found — report only, NOT fixed here (per harness policy)

Bug A — crash, K = 490, 491, 492, 493, 497 (plain-debug + ASan SEGV). NULL-deref on the hash-abort undo path, same stack for all five:

#0 __os_atomic_read (p=0xa4)            src/os/os_atomic.c:174
#1 __db_cursor  (MULTIVERSION check)    src/db/db_iface.c:370
#2 __ham_insdel_recover                 src/hash/hash_rec.c:84
#3 __txn_dispatch_undo / __txn_undo     src/txn/txn.c:1942,2036
#5 __txn_abort                          src/txn/txn.c:1242
#6 __txn_env_refresh                    src/txn/txn_region.c:252
#7 __env_refresh (at env close)         src/env/env_open.c:774

The wider workload leaves an aborted-hash txn to undo at env teardown; when an alloc on the undo path fails, __ham_insdel_recover opens a cursor on a dbp whose backing was never allocated. Repro: DB_FI_FAIL_AT=490 ./libtool --mode=execute ./fi_sweep --one.

Bug B — ASan leak (__db_create_internal OOM teardown). src/db/db_method.c:206 frees only dbp + dbp->mpf, never dbp->bt_internal / q_internal (the access-method private struct from __bam_db_create/__qam_db_create), and sets *dbpp = NULL so the caller can't free it either. Repro: DB_FI_FAIL_AT=503 ./.libs/fi_sweep --one.

Bug C — ASan leak (__db_join, src/db/db_join.c:93). Allocates the join cursor, then leaks it when a later alloc in the join wiring fails. Repro: DB_FI_FAIL_AT=641 ./.libs/fi_sweep --one.

Bug A is caught by both the forking sweep (CRASH) and ASan (SEGV); Bugs B/C are leaks only ASan sees (advisory). Full detail + fix shapes in test/faultinject/README.md.

CI

The full plain-debug sweep stays the gate; the ASan one-shot pass should sweep the full K = 1..M range (not stop at 260) so it reaches the new region past K = 480. That workflow update needs workflow token scope (agent pushes lack it), so it is staged as test/faultinject/faultinject.yml.workflow for a maintainer to sync into .github/workflows/faultinject.yml; the installed workflow file is left unchanged in this PR. Both jobs remain advisory (continue-on-error).

Validation

Built + run in the nix dev shell (--enable-debug --enable-faultinject, plain and CC=clang -fsanitize=address). No engine code changed.

gburd added 2 commits July 30, 2026 07:27
Extend the malloc-failure injection sweep workload beyond the basic
open/put/get/cursor/txn flow to drive the warmer error paths functional
tests miss:

  - secondary index: associate + get-by-secondary + pget
  - join cursor (DB->join)
  - bulk put/get (DB_MULTIPLE_KEY write buffer + cursor scan)
  - in-memory DB (NULL filename)
  - subdatabase open (named DB in a container file)
  - compaction (DB->compact with DB_FREE_SPACE)
  - DB->stat
  - 2PC prepare (txn->prepare then resolve)

Baseline allocation count grows M = 506 -> 947 (+441 sites, ~87% more
failure points swept). txn_recover is intentionally omitted: on a live
in-process prepared txn it double-resolves and panics the region; the
prepare path alone reaches the new 2PC alloc sites.

Ignore build_asan/ (the ASan build tree the CI/local ASan pass creates).
…an CI workflow

Update README with the expanded-workload results: M = 947, 861 clean-error,
81 tolerated, 5 CRASH, 0 HANG, 0 DIRTY. Document the new engine bugs the
wider sweep surfaced (report only, not fixed here):

  - Bug A (crash, K=490-493,497): NULL-deref in __db_cursor on the
    hash-abort undo path (__ham_insdel_recover -> __db_cursor on a
    half-built handle during __txn_abort at env teardown).
  - Bug B (ASan leak): __db_create_internal err: path frees dbp+mpf but
    not dbp->bt_internal / q_internal (the access-method private struct).
  - Bug C (ASan leak): __db_join leaks the join cursor on its OOM path.

The four original bugs (PR #52) no longer reproduce on the current tree.

CI: the full plain-debug sweep (K=1..M, ~46s) stays the gate. The ASan
one-shot pass should sweep the full K=1..M range (~2min, ~0.13s/run) so it
reaches the new leaks/crashes past K=480 instead of stopping at 260. Agent
pushes lack `workflow` scope, so that workflow update is staged as
test/faultinject/faultinject.yml.workflow for a maintainer to sync into
.github/workflows/faultinject.yml (the installed file is left unchanged).
@github-actions

Copy link
Copy Markdown

Coccinelle convention checks

No new violations. ✅

Resolved since baseline (2) -- update dist/cocci/baseline.txt to lock these in.
rule_mutex_unbalanced|MUTEX_UNBALANCED|src/crypto/mersenne/mt19937db.c|return (ret);
rule_mutex_unbalanced|MUTEX_UNBALANCED|src/mp/mp_register.c|return (ret);

@github-actions

Copy link
Copy Markdown

ABI diff vs v5.3.32 (libabigail — authoritative)

Removed exported symbols (nm -D, _NNNN version suffix normalized)

None.


Advisory: libabigail/nm is the authoritative binary-ABI check; Coccinelle is complementary source-level early warning. See dist/cocci/README.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant