mdbx: устранение регресса вероятности SIGSEGV при вытеснении/spilling страниц.

Ошибка внесена коммитом `a6f7d74a32a3cbcc310916a624a31302dbebfa07` от
2024-03-07 и присутствует в выпусках v0.13.1, v0.13.2, v0.13.3. Проблема
оставалась незамеченной из-за специфических условий и низкой вероятности
проявления.

Суть ошибки:

- функция cursor_touch() подготавливает стек страниц курсора к внесению
  изменений, при этом все страницы в стеке (от корневой до листовой
  в текущей позиции курсора) должны стать доступными для модификации.

- микрооптимизация добавленная коммитом пропускала обход стека, если
  корневая страница уже доступна для модификации, но это
  допустимо/корректно только при отсутствии в стеке вытесненных/spilled
  страниц.

- если же складывалась ситуация когда в стека была вытесненная
  некорневая страница, то она так и оставалась недоступной для записи и
  при попытке её изменения возникал SIGSEGV.
This commit is contained in:
Леонид Юрьев (Leonid Yuriev) 2025-01-27 02:25:32 +03:00
parent f6d91b3c5b
commit cb8eec6d11
4 changed files with 11 additions and 4 deletions

View File

@ -168,7 +168,7 @@ __hot int cursor_touch(MDBX_cursor *const mc, const MDBX_val *key, const MDBX_va
return err; return err;
} }
if (likely(mc->top >= 0) && !is_modifable(mc->txn, mc->pg[mc->top])) { if (likely(is_pointed(mc)) && ((mc->txn->flags & MDBX_TXN_SPILLS) || !is_modifable(mc->txn, mc->pg[mc->top]))) {
const int8_t top = mc->top; const int8_t top = mc->top;
mc->top = 0; mc->top = 0;
do { do {

View File

@ -50,6 +50,7 @@ int __must_check_result node_add_branch(MDBX_cursor *mc, size_t indx, const MDBX
is_subpage(mp) ? "sub-" : "", mp->pgno, indx, pgno, key ? key->iov_len : 0, DKEY_DEBUG(key)); is_subpage(mp) ? "sub-" : "", mp->pgno, indx, pgno, key ? key->iov_len : 0, DKEY_DEBUG(key));
cASSERT(mc, page_type(mp) == P_BRANCH); cASSERT(mc, page_type(mp) == P_BRANCH);
cASSERT(mc, mp->txnid >= mc->txn->front_txnid);
STATIC_ASSERT(NODESIZE % 2 == 0); STATIC_ASSERT(NODESIZE % 2 == 0);
/* Move higher pointers up one slot. */ /* Move higher pointers up one slot. */

View File

@ -457,6 +457,8 @@ static __always_inline pgr_t page_get_inline(const uint16_t ILL, const MDBX_curs
goto bailout; goto bailout;
} }
TRACE("dbi %zu, mc %p, page %u, %p", cursor_dbi(mc), __Wpedantic_format_voidptr(mc), pgno,
__Wpedantic_format_voidptr(r.page));
if (unlikely(mc->checking & z_pagecheck)) if (unlikely(mc->checking & z_pagecheck))
return check_page_complete(ILL, r.page, mc, front); return check_page_complete(ILL, r.page, mc, front);

View File

@ -72,6 +72,8 @@ static size_t spill_cursor_keep(const MDBX_txn *const txn, const MDBX_cursor *mc
intptr_t i = 0; intptr_t i = 0;
do { do {
mp = mc->pg[i]; mp = mc->pg[i];
TRACE("dbi %zu, mc-%p[%zu], page %u %p", cursor_dbi(mc), __Wpedantic_format_voidptr(mc), i, mp->pgno,
__Wpedantic_format_voidptr(mp));
tASSERT(txn, !is_subpage(mp)); tASSERT(txn, !is_subpage(mp));
if (is_modifable(txn, mp)) { if (is_modifable(txn, mp)) {
size_t const n = dpl_search(txn, mp->pgno); size_t const n = dpl_search(txn, mp->pgno);
@ -81,16 +83,18 @@ static size_t spill_cursor_keep(const MDBX_txn *const txn, const MDBX_cursor *mc
*ptr = txn->wr.dirtylru; *ptr = txn->wr.dirtylru;
tASSERT(txn, dpl_age(txn, n) == 0); tASSERT(txn, dpl_age(txn, n) == 0);
++keep; ++keep;
DEBUG("keep page %" PRIaPGNO " (%p), dbi %zu, %scursor %p[%zu]", mp->pgno, __Wpedantic_format_voidptr(mp),
cursor_dbi(mc), is_inner(mc) ? "sub-" : "", __Wpedantic_format_voidptr(mc), i);
} }
} }
} while (++i <= mc->top); } while (++i <= mc->top);
tASSERT(txn, is_leaf(mp)); tASSERT(txn, is_leaf(mp));
if (!mc->subcur || mc->ki[mc->top] >= page_numkeys(mp)) if (!inner_pointed(mc))
break;
if (!(node_flags(page_node(mp, mc->ki[mc->top])) & N_TREE))
break; break;
mc = &mc->subcur->cursor; mc = &mc->subcur->cursor;
if (is_subpage(mc->pg[0]))
break;
} }
return keep; return keep;
} }