Skip to content

fix(upgrade): __db_set_lastpgno stored page count, not last page number - #77

Merged
gburd merged 1 commit into
masterfrom
fix/db-upg-lastpgno
Jul 29, 2026
Merged

fix(upgrade): __db_set_lastpgno stored page count, not last page number#77
gburd merged 1 commit into
masterfrom
fix/db-upg-lastpgno

Conversation

@gburd

@gburd gburd commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Long-standing latent defect (byte-identical to upstream BDB 4.7/4.8), found by the access-method upgrade coverage work (PR #75).

__db_lastpgno() returns the page count (bytes/pagesize) — its other caller __db_page_pass uses it correctly as a loop bound (for i=0; i<pgno_last). But __db_set_lastpgno assigned that count straight into meta.last_pgno, which the engine treats as the last valid page number (0-indexed). For an N-page file this left last_pgno = N instead of N-1.

On the v8→v9 upgrade path (__db_upgrade case 8), db_verify then rejects the file under HAVE_FTRUNCATE: Page 0: last_pgno is not correct: N != N-1. The hash path masked it (its v8→v9 pass goes through mpool, which recomputes last_pgno on close).

Fix: store the last page number (count - 1), mapping an impossible 0-page file to PGNO_INVALID rather than underflowing.

Verified

  • A v8 meta upgraded via db_upgrade now passes db_verify (was "last_pgno is not correct")
  • Normal v9 upgrade + test001 btree/hash unaffected

(The v7-flip synthetic fixture still fails verify — that's a known synthetic-fixture artifact per PR #75, not this bug; flipping a real v9 file to v7 doesn't produce a byte-accurate v7 page layout.)

__db_lastpgno() returns the page COUNT (file bytes / pagesize) -- its other
caller __db_page_pass uses it correctly as a loop bound (for i=0; i<pgno_last).
But __db_set_lastpgno assigned that count directly into meta.last_pgno, which
the rest of the engine treats as the last valid page NUMBER (0-indexed).  For an
N-page file this left last_pgno = N instead of N-1 -- one too high.

This is on the v8->v9 upgrade path (__db_upgrade case 8, a pure metadata pass).
db_verify then rejects the upgraded file under HAVE_FTRUNCATE with
"Page 0: last_pgno is not correct: N != N-1".  The bug is byte-identical to
upstream Berkeley DB 4.7/4.8, i.e. a long-standing latent defect; the hash
upgrade path happened to mask it (its v8->v9 pass goes through mpool, which
recomputes last_pgno on close).

Fix: store the last page NUMBER (count - 1), mapping an impossible 0-page file
to PGNO_INVALID rather than underflowing.

Found by the access-method upgrade coverage work (PR #75, which exercises the
per-version transforms via synthetic old-format fixtures).

Verified: a v8 meta upgraded with db_upgrade now passes db_verify (was "last_pgno
is not correct"); normal v9 upgrade + btree/hash test001 unaffected.
@gburd
gburd merged commit 00c6df0 into master Jul 29, 2026
47 of 49 checks passed
@gburd
gburd deleted the fix/db-upg-lastpgno branch July 29, 2026 11:16
@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.31 (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