diff --git a/ChangeLog.md b/ChangeLog.md index 157e2235..08c2aaf9 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,16 @@ and [by Yandex](https://translated.turbopages.org/proxy_u/ru-en.en/https/gitflic изменения геометрии другим процессом с увеличением верхнего размера БД и увеличением БД больше предыдущего лимита. + - Доработка `mdbx_close_dbi()` для возврата ошибки при попытке закрыть + dbi-дескриптор таблицы, созданной и/или измененной в ещё выполняющейся + транзакции. Такое преждевременное закрытие дескриптора является неверным + использованием API и нарушением контракта/предусловий сформулированных + в описании `mdbx_close_dbi()`. Однако, вместо возврата ошибки + выполнялось некорректное закрытие дескриптора, что могло приводить к + созданию таблицы с пустым именем, утечки страниц БД и/или нарушению + структуры b-tree (неверной ссылкой на корень таблицы). + + -------------------------------------------------------------------------------- diff --git a/src/core.c b/src/core.c index 2b6bf969..b0e9de7b 100644 --- a/src/core.c +++ b/src/core.c @@ -23134,9 +23134,29 @@ int mdbx_dbi_close(MDBX_env *env, MDBX_dbi dbi) { rc = osal_fastmutex_acquire(&env->me_dbi_lock); if (likely(rc == MDBX_SUCCESS)) { - rc = (dbi < env->me_maxdbs && (env->me_dbflags[dbi] & DB_VALID)) - ? dbi_close_locked(env, dbi) - : MDBX_BAD_DBI; + retry: + rc = MDBX_BAD_DBI; + if (dbi < env->me_maxdbs && (env->me_dbflags[dbi] & DB_VALID)) { + const MDBX_txn *const hazard = env->me_txn; + osal_compiler_barrier(); + if (env->me_txn0 && (env->me_txn0->mt_flags & MDBX_TXN_FINISHED) == 0) { + if (env->me_txn0->mt_dbistate[dbi] & (DBI_DIRTY | DBI_CREAT)) + goto bailout_dirty_dbi; + osal_memory_barrier(); + if (unlikely(hazard != env->me_txn)) + goto retry; + if (hazard != env->me_txn0 && hazard && + (hazard->mt_flags & MDBX_TXN_FINISHED) == 0 && + hazard->mt_signature == MDBX_MT_SIGNATURE && + (hazard->mt_dbistate[dbi] & (DBI_DIRTY | DBI_CREAT))) + goto bailout_dirty_dbi; + osal_compiler_barrier(); + if (unlikely(hazard != env->me_txn)) + goto retry; + } + rc = dbi_close_locked(env, dbi); + } + bailout_dirty_dbi: ENSURE(env, osal_fastmutex_release(&env->me_dbi_lock) == MDBX_SUCCESS); } return rc;