diff --git a/src/bits.h b/src/bits.h index d43f4e99..df0d44cd 100644 --- a/src/bits.h +++ b/src/bits.h @@ -512,6 +512,7 @@ typedef MDBX_ID2 *MDBX_ID2L; #define MDBX_PNL_DB_MAX (MDBX_PNL_DB_SIZE - 1) #define MDBX_PNL_UM_MAX (MDBX_PNL_UM_SIZE - 1) +#define MDBX_PNL_MAX (MAX_PAGENO / 4) #define MDBX_PNL_SIZEOF(pl) (((pl)[0] + 1) * sizeof(pgno_t)) #define MDBX_PNL_IS_ZERO(pl) ((pl)[0] == 0) diff --git a/src/mdbx.c b/src/mdbx.c index 1d8cdf8a..34aad669 100644 --- a/src/mdbx.c +++ b/src/mdbx.c @@ -501,6 +501,7 @@ __cold void mdbx_rthc_remove(const mdbx_thread_key_t key) { * Allocates memory for an PNL of the given size. * Returns PNL on success, NULL on failure. */ static MDBX_PNL mdbx_pnl_alloc(size_t size) { + assert(size <= MDBX_PNL_MAX); MDBX_PNL pl = malloc((size + 2) * sizeof(pgno_t)); if (likely(pl)) { *pl++ = (pgno_t)size; @@ -540,8 +541,11 @@ static __inline void mdbx_pnl_xappend(MDBX_PNL pl, pgno_t id) { pl[pl[0] += 1] = id; } -static bool mdbx_pnl_check(MDBX_PNL pl) { +static bool mdbx_pnl_check(MDBX_PNL pl, bool allocated) { if (pl) { + if (allocated) { + assert(pl[0] <= MDBX_PNL_MAX && pl[0] <= pl[-1]); + } for (const pgno_t *ptr = pl + pl[0]; --ptr > pl;) { assert(MDBX_PNL_ORDERED(ptr[0], ptr[1])); assert(ptr[0] >= NUM_METAS); @@ -555,6 +559,7 @@ static bool mdbx_pnl_check(MDBX_PNL pl) { /* Sort an PNL. * [in,out] pnl The PNL to sort. */ static void __hot mdbx_pnl_sort(MDBX_PNL pnl) { + assert(pnl[0] <= MDBX_PNL_MAX && pnl[0] <= pnl[-1]); /* Max possible depth of int-indexed tree * 2 items/level */ int istack[sizeof(int) * CHAR_BIT * 2]; int i, j, k, l, ir, jstack; @@ -629,7 +634,7 @@ static void __hot mdbx_pnl_sort(MDBX_PNL pnl) { } #undef PNL_SMALL #undef PNL_SWAP - assert(mdbx_pnl_check(pnl)); + assert(mdbx_pnl_check(pnl, true)); } /* Search for an ID in an PNL. @@ -637,7 +642,7 @@ static void __hot mdbx_pnl_sort(MDBX_PNL pnl) { * [in] id The ID to search for. * Returns The index of the first ID greater than or equal to id. */ static unsigned __hot mdbx_pnl_search(MDBX_PNL pnl, pgno_t id) { - assert(mdbx_pnl_check(pnl)); + assert(mdbx_pnl_check(pnl, true)); /* binary search of id in pl * if found, returns position of id @@ -687,18 +692,23 @@ static void mdbx_pnl_shrink(MDBX_PNL *ppl) { /* Grow an PNL. * Return the PNL to the size growed by given number. * [in,out] ppl Address of the PNL to grow. */ -static int mdbx_pnl_grow(MDBX_PNL *ppl, size_t num) { +static int __must_check_result mdbx_pnl_grow(MDBX_PNL *ppl, size_t num) { MDBX_PNL idn = *ppl - 1; + assert(idn[0] <= MDBX_PNL_MAX && idn[0] <= idn[-1]); + assert(num <= MDBX_PNL_MAX); + num += *idn; + if (unlikely(num > MDBX_PNL_MAX)) + return MDBX_TXN_FULL; /* grow it */ - idn = realloc(idn, (*idn + num + 2) * sizeof(pgno_t)); + idn = realloc(idn, (num + 2) * sizeof(pgno_t)); if (unlikely(!idn)) return MDBX_ENOMEM; *idn++ += (pgno_t)num; *ppl = idn; - return 0; + return MDBX_SUCCESS; } -static int mdbx_txl_grow(MDBX_TXL *ptr, size_t num) { +static int __must_check_result mdbx_txl_grow(MDBX_TXL *ptr, size_t num) { MDBX_TXL list = *ptr - 1; /* grow it */ list = realloc(list, ((size_t)*list + num + 2) * sizeof(txnid_t)); @@ -706,85 +716,96 @@ static int mdbx_txl_grow(MDBX_TXL *ptr, size_t num) { return MDBX_ENOMEM; *list++ += num; *ptr = list; - return 0; + return MDBX_SUCCESS; } /* Make room for num additional elements in an PNL. * [in,out] ppl Address of the PNL. * [in] num Number of elements to make room for. * Returns 0 on success, MDBX_ENOMEM on failure. */ -static int mdbx_pnl_need(MDBX_PNL *ppl, size_t num) { +static int __must_check_result mdbx_pnl_need(MDBX_PNL *ppl, size_t num) { MDBX_PNL pl = *ppl; + assert(pl[0] <= MDBX_PNL_MAX && pl[0] <= pl[-1]); + assert(num <= MDBX_PNL_MAX); num += pl[0]; if (unlikely(num > pl[-1])) { - num = (num + num / 4 + (256 + 2)) & -256; + if (unlikely(num > MDBX_PNL_MAX)) + return MDBX_TXN_FULL; + num = (num + num / 4 + (256 + 2)) & ~255u; + num = (num < MDBX_PNL_MAX + 2) ? num : MDBX_PNL_MAX + 2; pl = realloc(pl - 1, num * sizeof(pgno_t)); if (unlikely(!pl)) return MDBX_ENOMEM; *pl++ = (pgno_t)num - 2; *ppl = pl; } - return 0; + return MDBX_SUCCESS; } /* Append an ID onto an PNL. * [in,out] ppl Address of the PNL to append to. * [in] id The ID to append. * Returns 0 on success, MDBX_ENOMEM if the PNL is too large. */ -static int mdbx_pnl_append(MDBX_PNL *ppl, pgno_t id) { +static int __must_check_result mdbx_pnl_append(MDBX_PNL *ppl, pgno_t id) { MDBX_PNL pl = *ppl; /* Too big? */ if (unlikely(pl[0] >= pl[-1])) { - if (mdbx_pnl_grow(ppl, MDBX_PNL_UM_MAX)) - return MDBX_ENOMEM; + int rc = mdbx_pnl_grow(ppl, MDBX_PNL_UM_MAX); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; pl = *ppl; } pl[0]++; pl[pl[0]] = id; - return 0; + return MDBX_SUCCESS; } -static int mdbx_txl_append(MDBX_TXL *ptr, txnid_t id) { +static int __must_check_result mdbx_txl_append(MDBX_TXL *ptr, txnid_t id) { MDBX_TXL list = *ptr; /* Too big? */ if (unlikely(list[0] >= list[-1])) { - if (mdbx_txl_grow(ptr, (size_t)list[0])) - return MDBX_ENOMEM; + int rc = mdbx_txl_grow(ptr, (size_t)list[0]); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; list = *ptr; } list[0]++; list[list[0]] = id; - return 0; + return MDBX_SUCCESS; } /* Append an PNL onto an PNL. * [in,out] ppl Address of the PNL to append to. * [in] app The PNL to append. * Returns 0 on success, MDBX_ENOMEM if the PNL is too large. */ -static int mdbx_pnl_append_list(MDBX_PNL *ppl, MDBX_PNL app) { +static int __must_check_result mdbx_pnl_append_list(MDBX_PNL *ppl, + MDBX_PNL app) { MDBX_PNL pnl = *ppl; /* Too big? */ if (unlikely(pnl[0] + app[0] >= pnl[-1])) { - if (mdbx_pnl_grow(ppl, app[0])) - return MDBX_ENOMEM; + int rc = mdbx_pnl_grow(ppl, app[0]); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; pnl = *ppl; } memcpy(&pnl[pnl[0] + 1], &app[1], app[0] * sizeof(pgno_t)); pnl[0] += app[0]; - return 0; + return MDBX_SUCCESS; } -static int mdbx_txl_append_list(MDBX_TXL *ptr, MDBX_TXL append) { +static int __must_check_result mdbx_txl_append_list(MDBX_TXL *ptr, + MDBX_TXL append) { MDBX_TXL list = *ptr; /* Too big? */ if (unlikely(list[0] + append[0] >= list[-1])) { - if (mdbx_txl_grow(ptr, (size_t)append[0])) - return MDBX_ENOMEM; + int rc = mdbx_txl_grow(ptr, (size_t)append[0]); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; list = *ptr; } memcpy(&list[list[0] + 1], &append[1], (size_t)append[0] * sizeof(txnid_t)); list[0] += append[0]; - return 0; + return MDBX_SUCCESS; } /* Append an ID range onto an PNL. @@ -792,27 +813,29 @@ static int mdbx_txl_append_list(MDBX_TXL *ptr, MDBX_TXL append) { * [in] id The lowest ID to append. * [in] n Number of IDs to append. * Returns 0 on success, MDBX_ENOMEM if the PNL is too large. */ -static int mdbx_pnl_append_range(MDBX_PNL *ppl, pgno_t id, size_t n) { +static int __must_check_result mdbx_pnl_append_range(MDBX_PNL *ppl, pgno_t id, + size_t n) { pgno_t *pnl = *ppl, len = pnl[0]; /* Too big? */ if (unlikely(len + n > pnl[-1])) { - if (mdbx_pnl_grow(ppl, n | MDBX_PNL_UM_MAX)) - return MDBX_ENOMEM; + int rc = mdbx_pnl_grow(ppl, n | MDBX_PNL_UM_MAX); + if (unlikely(rc != MDBX_SUCCESS)) + return rc; pnl = *ppl; } pnl[0] = len + (pgno_t)n; pnl += len; while (n) pnl[n--] = id++; - return 0; + return MDBX_SUCCESS; } /* Merge an PNL onto an PNL. The destination PNL must be big enough. * [in] pl The PNL to merge into. * [in] merge The PNL to merge. */ static void __hot mdbx_pnl_xmerge(MDBX_PNL pnl, MDBX_PNL merge) { - assert(mdbx_pnl_check(pnl)); - assert(mdbx_pnl_check(merge)); + assert(mdbx_pnl_check(pnl, true)); + assert(mdbx_pnl_check(merge, false)); pgno_t old_id, merge_id, i = merge[0], j = pnl[0], k = i + j, total = k; pnl[0] = MDBX_PNL_ASCENDING ? 0 : ~(pgno_t)0; /* delimiter for pl scan below */ @@ -824,7 +847,7 @@ static void __hot mdbx_pnl_xmerge(MDBX_PNL pnl, MDBX_PNL merge) { pnl[k--] = merge_id; } pnl[0] = total; - assert(mdbx_pnl_check(pnl)); + assert(mdbx_pnl_check(pnl, true)); } /* Search for an ID in an ID2L. @@ -2171,7 +2194,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, } } - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); pgno_t pgno, *repg_list = env->me_reclaimed_pglist; unsigned repg_pos = 0, repg_len = repg_list ? repg_list[0] : 0; txnid_t oldest = 0, last = 0; @@ -2191,7 +2214,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, /* Seek a big enough contiguous page range. * Prefer pages with lower pgno. */ - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); if (likely(flags & MDBX_ALLOC_CACHE) && repg_len > wanna_range && (!(flags & MDBX_COALESCE) || op == MDBX_FIRST)) { #if MDBX_PNL_ASCENDING @@ -2305,7 +2328,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, pgno_t *re_pnl = (pgno_t *)data.iov_base; mdbx_tassert(txn, re_pnl[0] == 0 || data.iov_len == (re_pnl[0] + 1) * sizeof(pgno_t)); - mdbx_tassert(txn, mdbx_pnl_check(re_pnl)); + mdbx_tassert(txn, mdbx_pnl_check(re_pnl, false)); repg_pos = re_pnl[0]; if (!repg_list) { if (unlikely(!(env->me_reclaimed_pglist = repg_list = @@ -2375,7 +2398,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, mdbx_info("refunded %" PRIaPGNO " pages: %" PRIaPGNO " -> %" PRIaPGNO, tail - txn->mt_next_pgno, tail, txn->mt_next_pgno); txn->mt_next_pgno = tail; - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); } } @@ -2485,7 +2508,7 @@ static int mdbx_page_alloc(MDBX_cursor *mc, unsigned num, MDBX_page **mp, } fail: - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); if (mp) { *mp = NULL; txn->mt_flags |= MDBX_TXN_ERROR; @@ -2516,7 +2539,7 @@ done: repg_list[0] = repg_len -= num; for (unsigned i = repg_pos - num; i < repg_len;) repg_list[++i] = repg_list[++repg_pos]; - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); } else { txn->mt_next_pgno = pgno + num; mdbx_assert(env, txn->mt_next_pgno <= txn->mt_end_pgno); @@ -2533,7 +2556,7 @@ done: mdbx_page_dirty(txn, np); *mp = np; - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); return MDBX_SUCCESS; } @@ -3510,14 +3533,14 @@ static int mdbx_freelist_save(MDBX_txn *txn) { (env->me_flags & (MDBX_NOMEMINIT | MDBX_WRITEMAP)) ? SSIZE_MAX : env->me_maxfree_1pg; - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); again_on_freelist_change: - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); while (1) { /* Come back here after each Put() in case freelist changed */ MDBX_val key, data; - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); if (!lifo) { /* If using records from freeDB which we have not yet deleted, * now delete them and any we reserved for me_reclaimed_pglist. */ @@ -3560,7 +3583,7 @@ again_on_freelist_change: } } - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); if (txn->mt_loose_pages) { /* Return loose page numbers to me_reclaimed_pglist, * though usually none are left at this point. @@ -3613,7 +3636,7 @@ again_on_freelist_change: txn->mt_loose_count = 0; } - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); if (env->me_reclaimed_pglist) { /* Refund suitable pages into "unallocated" space */ pgno_t tail = txn->mt_next_pgno; @@ -3641,7 +3664,7 @@ again_on_freelist_change: mdbx_info("refunded %" PRIaPGNO " pages: %" PRIaPGNO " -> %" PRIaPGNO, tail - txn->mt_next_pgno, tail, txn->mt_next_pgno); txn->mt_next_pgno = tail; - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); } } @@ -3682,7 +3705,7 @@ again_on_freelist_change: continue; } - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); const intptr_t rpl_len = (env->me_reclaimed_pglist ? env->me_reclaimed_pglist[0] : 0) + txn->mt_loose_count; @@ -3760,7 +3783,7 @@ again_on_freelist_change: key.iov_base = &head_id; data.iov_len = (head_room + 1) * sizeof(pgno_t); rc = mdbx_cursor_put(&mc, &key, &data, MDBX_RESERVE); - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); if (unlikely(rc)) goto bailout; @@ -3780,7 +3803,7 @@ again_on_freelist_change: /* Fill in the reserved me_reclaimed_pglist records */ rc = MDBX_SUCCESS; - mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist)); + mdbx_tassert(txn, mdbx_pnl_check(env->me_reclaimed_pglist, true)); if (env->me_reclaimed_pglist && env->me_reclaimed_pglist[0]) { MDBX_val key, data; key.iov_len = data.iov_len = 0; /* avoid MSVC warning */ @@ -3831,11 +3854,11 @@ again_on_freelist_change: data.iov_base = rpl_end; pgno_t save = rpl_end[0]; rpl_end[0] = (pgno_t)chunk_len; - mdbx_tassert(txn, mdbx_pnl_check(rpl_end)); + mdbx_tassert(txn, mdbx_pnl_check(rpl_end, false)); mc.mc_flags |= C_RECLAIMING; rc = mdbx_cursor_put(&mc, &key, &data, MDBX_CURRENT); mc.mc_flags ^= C_RECLAIMING; - mdbx_tassert(txn, mdbx_pnl_check(rpl_end)); + mdbx_tassert(txn, mdbx_pnl_check(rpl_end, false)); mdbx_tassert( txn, cleanup_reclaimed_pos == (txn->mt_lifo_reclaimed ? txn->mt_lifo_reclaimed[0] : 0)); diff --git a/src/tools/mdbx_chk.c b/src/tools/mdbx_chk.c index 51096c40..772102cc 100644 --- a/src/tools/mdbx_chk.c +++ b/src/tools/mdbx_chk.c @@ -340,7 +340,7 @@ static int handle_freedb(const uint64_t record_number, const MDBX_val *key, data->iov_len); else { const pgno_t number = *iptr++; - if (number >= MDBX_PNL_UM_MAX) + if (number < 1 || number > MDBX_PNL_MAX) problem_add("entry", record_number, "wrong idl length", "%" PRIiPTR "", number); else if ((number + 1) * sizeof(pgno_t) != data->iov_len)