1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-31185 rw_trx_hash_t::find() unpins pins too early

rw_trx_hash_t::find() acquires element->mutex, then unpins pins, used for
lf_hash element search. After that the "element" can be deallocated and
reused by some other thread.

If we take a look rw_trx_hash_t::insert()->lf_hash_insert()->lf_alloc_new()
calls, we will not find any element->mutex acquisition, as it was not
initialized yet before it's allocation. rw_trx_hash_t::insert() can reuse
the chunk, unpinned in rw_trx_hash_t::find().

The scenario is the following:

1. Thread 1 have just executed lf_hash_search() in
rw_trx_hash_t::find(), but have not acquired element->mutex yet.
2. Thread 2 have removed the element from hash table with
rw_trx_hash_t::erase() call.
3. Thread 1 acquired element->mutex and unpinned pin 2 pin with
lf_hash_search_unpin(pins) call.
4. Some thread purged memory of the element.
5. Thread 3 reused the memory for the element, filled element->id,
element->trx.
6. Thread 1 crashes with failed "DBUG_ASSERT(trx_id == trx->id)"
assertion.

Note that trx_t objects are also reused, see the code around trx_pools
for details.

The fix is to invoke "lf_hash_search_unpin(pins);" after element->trx is
stored in local variable in rw_trx_hash_t::find().

Reviewed by: Nikita Malyavin, Marko Mäkelä.
This commit is contained in:
Vlad Lesin
2023-05-12 12:11:53 +03:00
parent f4ce1e487e
commit b54e7b0cea
6 changed files with 144 additions and 1 deletions

View File

@@ -824,6 +824,10 @@ retry:
element= (TDC_element*) lf_hash_search_using_hash_value(&tdc_hash,
thd->tdc_hash_pins, hash_value, (uchar*) key, key_length);
/* It's safe to unpin the pins here, because an empty element was inserted
above, "empty" means at least element->share = 0. Some other thread can't
delete it while element->share == 0. And element->share is also protected
with element->LOCK_table_share mutex. */
lf_hash_search_unpin(thd->tdc_hash_pins);
DBUG_ASSERT(element);