libdb 2026.09
Warning
Known regression in this release — queue extent verification is disabled.
The queue extent-scan DoS fix (#160) clamps the verify scan to vdp->last_pgno, which is the page count of the main .db file. For an extent queue that file holds only the meta page, so the bound collapses to 0 and __qam_vrfy_walkqueue skips the entire extent walk: db_verify reports success on a queue with a corrupted extent page. Confirmed on a 20,000-record / 770-extent queue.
Only affects verification of Queue databases that use extents (DB->set_q_extentsize). No effect on normal reads/writes, other access methods, or on-disk data. Fixed in 5.3.36 (#163 clamps the verify scan, #168 bounds the cursor read path) — upgrade.
A correctness release. It fixes every externally reported defect against 5.3.34, two crash-on-valid-API NULL dereferences found while raising test coverage, and three hardening items deferred from the security review.
Every fix ships with a regression test that runs in CI, and each test was verified to fail when its fix is reverted — a passing test proves nothing unless it can fail.
If you are running 5.3.34, upgrade: #136 could silently commit a non-serializable schedule, and #137/#138 could exhaust the mutex region.
Externally reported (#136–#140, all closed)
#136 — a write skew could COMMIT under DB_TXN_SNAPSHOT. The commit-time pivot check read its two conflict flags under TXN_SYSTEM_LOCK and released it, but the TXN_RUNNING → TXN_COMMITTED transition happens far later in __txn_end. The writer-side "the reader will abort" optimization trusted that stale status and deferred to a check that had already happened, so both transactions escaped. A passing check now publishes TXN_DTL_SICHECKED inside the same critical section, and both writer sites — the lock table and the MVCC mirror in __memp_si_rwconflict — test that instead of the raw status.
Two design notes worth recording: re-checking later is not merely worse but incorrect (by then the log is flushed and read locks are released, so the window is not abortable), and testing status != TXN_RUNNING would spuriously abort writers whose edge points into an aborted transaction.
#137 / #138 — resource exhaustion. Committed-reader lockers were never reclaimed, so sequential read-only snapshot transactions exhausted the mutex region and txn_begin returned ENOMEM; and __txn_reap_si_details freed a transaction detail without releasing its MVCC mutex. A third leak, reported by nobody, surfaced while validating those: si_ref was not decremented when a transaction upgraded its own SIREAD marker to WRITE.
#139 — a hash lookup missed a key that was present. On a legacy unsorted hash page with a custom comparator, DB->get returned DB_NOTFOUND and put(DB_NOOVERWRITE) could store a duplicate. The comparator's result was dropped — and fixing only that turned the false negative into a false positive, because the stored-key DBT was built with the search key's length, so a prefix compared equal against a truncated view of the stored item. Both halves are fixed.
#140 — heap buffer overflow. DB_LOCK_SIREAD was not counted when sizing the replication commit lock list, yet still consumed a slot. Reachable in release builds. Sizing and population now share one IS_WRITELOCK predicate so they agree by construction, and the diagnostic-only DB_ASSERT became __env_panic.
The root-cause class is closed as well: all 19 lock-mode enumeration sites were audited (4 were wrong), and CI now fails if a new DB_LOCK_* mode appears without updating the inventory.
Found while raising coverage (#148, #149)
- #148 —
set_lk_priority()/get_lk_priority()SIGSEGV on a locker id with no live locker.__lock_getlocker(create=0)reports not found by returning 0 with a NULL locker. The check is deliberately local to those two functions, because__lock_vec_pppasses the same possibly-NULL locker into__lock_vec, which handles it on purpose. - #149 —
rep_get_nsites()SIGSEGV on a repmgr-configured but unopened environment (ENV_NOT_CONFIGUREDexpands to nothing before open); and__cdsgroup_begininstalled only 8 ofDB_TXN's 12 methods, so callingget_priority,set_priority,set_commit_tokenorset_txn_lsnpon a CDS group transaction jumped through a NULL pointer.
Hardening
- Recovery no longer receives a half-opened DB handle.
__dbreg_do_openregisters a handle before its open finishes, so a failure after that point left an entry whose__db_opennever completed;__dbreg_id_to_dbreturned it withret == 0andREC_INTROdereferenced its unattached mpool file. The ASan fault-injection sweep now passes at all 947 failure points: 0 crashes, 0 hangs, 0 dirty teardowns. - Queue verification no longer scans billions of pages. A wrapped queue meta page made
__qam_vrfy_walkqueuescan to the page holding recnoUINT32_MAX, one page at a time. Clamped to the real last page, and verified not to reject legitimate queues — including one with 639 real extent files. A separate DoS in the cursor read path was filed as #159 with a reproducer, and is fixed in 5.3.36 (#168). - Encryption IVs are seeded from the OS CSPRNG (
getrandom,arc4random_buf,/dev/urandom,RtlGenRandom).
Testing
Three new tiers, each proven to reproduce its target defect before the fix landed:
| Tier | What it checks |
|---|---|
test/isolation |
an anomaly checker validating concurrent schedules against some serial order |
test/soak |
resource accounting over tens of thousands of transactions |
test/lockmatrix |
an exhaustive lock-mode matrix under ASan |
Four test/db regression runners that no CI job had ever invoked are now wired up and made portable to macOS (three genuine portability bugs fell out: .dylib vs .so, no GNU timeout, and a baked-in .dylib install name that overrides -rpath). The crash-seed gate grew a per-seed timeout, so a runaway scan now fails CI instead of hanging it.
Also
db 2.3.16anddb 2.4.14added to the historical import timeline (#135).- The serializability claims retracted ahead of these fixes are restored, now that each is covered by a test.
Qualification
Qualified from a pristine clone: autoconf (--enable-cxx) and meson both clean; test/db 6/6; isolation, lockmatrix and soak (5 workloads, 0 unexpected) all pass; crash-seed gate 9/9; fi_sweep 947/947 clean; DST test_sim_rng + test_sim_crash_recover; TCL ssi001/002/009, txn001–003, lock001–003, recd001/002 and test001 across btree/hash/queue/recno all fail=0; docs build with man-coverage and spelling gates passing.