1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00
Files
mariadb/sql/sql_cache.cc
Aleksey Midenkov 4b1b67e481 MDEV-25365 Server hangs or crashes in Query_cache::move_by_type upon FLUSH QUERY CACHE
Caused by Bug#988 in 702b5ad.

How it works

QUERY blocks has all its tables in the array coming after
Query_cache_block header. Each such table element
(Query_cache_block_table) is also an element of doubly-linked circular
list associated with TABLE block used to find all queries by a
table. TABLE block data() is actually Query_cache_table: identificator
for table elements by real name. Every element in the circular list
has `parent` set to such Query_cache_table, except the list root. The
list root is the only Query_cache_block_table element owned by a TABLE
block, all other elements are owned by QUERY blocks. Thus TABLE block
n_tables is always 1.

FREE blocks are transformed into gap pointed by `border`. All the
QUERY and TABLE blocks are moved in place of the gap providing free
space defragmentation. Moving is done by creating new block at the
location of the gap and then doing memmove() from an old block to new
block. This may involve blocks overlap when the distance between the
blocks is less than the block size.

What was broken

When the old and the new QUERY blocks overlap, pointer can be in range
of both of them and checking the range does not guarantee affiliation
of the pointer with the old block. Such intra-block checks added by
Bug#988 can wrongly work on an already updated pointer.

In the test case small gap allows both tables 16 and 17 to be in the
overlapping region of old and new blocks. Iteration 16 updated
`prev->next` on the normal manner and the `prev` was table 17.
Iteration 17 sees this `next` as a pointer coming from an old block
though really it is already for a new block (as small gap shift allows
table 16 to be in both blocks), and it shifts it to the wrong
location. That have broken TABLE block circular chain.

How it was fixed

The fix modifies intra-block checks to work only in the forward
direction. F.ex., current table jx references intra-block by `next`
another table jy in later iteration (jx < jy) and we shift this `next`
pointer. Then by normal code (pre-Bug#988) we update `next->prev`. At
iteration jy we skip intra-block check for `prev` as it is in the past
iteration and was updated by iteration jx. The same happens for the
opposite direction, there is no semantic difference between `next` and
`prev` pointers.
2026-03-27 15:08:28 +03:00

163 KiB