From bad82f2285544bf4f2e7bf5915f60a285a2c4431 Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Wed, 29 Jul 2026 08:52:47 -0400 Subject: [PATCH] fix(mpool): cache shrink detached one region past the end (SIGSEGV) __memp_remove_region detached dbmp->reginfo[mp->nreg] -- but live cache regions occupy reginfo[0 .. nreg-1], so reginfo[nreg] is one slot PAST the last region. __env_region_detach then munmap()'d that stale/garbage REGINFO, reliably crashing (SIGSEGV in __os_detach) whenever the cache was shrunk to fewer regions (DB_ENV->resize_cache / memp_resize downward). The bug was a mirror-image copy of the add path: __memp_add_region correctly uses reginfo[mp->nreg] because it is initializing the NEXT, not-yet-counted slot before doing mp->nreg++. Removal is the opposite -- it must index the last LIVE region, reginfo[nreg-1], and then decrement nreg. Fix: detach reginfo[mp->nreg - 1]. Found by the MVCC/cache-resize coverage work (PR #81), which grows-only to dodge this crash. Verified: grow-then-shrink (the documented repro) now completes cleanly instead of SIGSEGV; data survives repeated grow/shrink cycles; memp001/003 + mvcc001 pass. --- src/mp/mp_resize.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mp/mp_resize.c b/src/mp/mp_resize.c index 8a2326251..a0961a6cc 100644 --- a/src/mp/mp_resize.c +++ b/src/mp/mp_resize.c @@ -447,8 +447,15 @@ __memp_remove_region(dbmp) if ((ret = __memp_remove_bucket(dbmp)) != 0) return (ret); - /* Detach from the region then destroy it. */ - infop = &dbmp->reginfo[mp->nreg]; + /* + * Detach from the region then destroy it. Live cache regions occupy + * reginfo[0 .. nreg-1], so the last one -- the region being removed -- + * is at index nreg-1. (The add path uses reginfo[nreg] because it is + * initializing the next, not-yet-counted slot before incrementing nreg; + * removal is the mirror and must index the last LIVE region, else it + * detaches/munmaps a stale slot one past the end -> SIGSEGV.) + */ + infop = &dbmp->reginfo[mp->nreg - 1]; if (F_ISSET(env, ENV_PRIVATE)) { hp = R_ADDR(infop, ((MPOOL*)infop->primary)->htab); for (i = 0; i < env->dbenv->mp_mtxcount; i++)