mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
A worker's handler was destroyed with its parallel scan still open. exec() and scan_only() jump to their exit labels when parallel_init_worker() fails, and the commonest failure is HA_ERR_END_OF_FILE: no chunk was left for this worker, which happens all the time on small tables. InnoDB has allocated m_pscan_worker by then, so the scan was left for ~ha_innobase() -> parallel_scan_free(), which runs after close() has freed m_prebuilt. parallel_scan_free() says it drops the worker without end() for exactly that reason, but ~Parallel_scan_worker() called end() anyway, and end() writes m_prebuilt->pscan_chunk_clamp: a heap-use-after-free that ASAN reports on every such worker. In a build without ASAN the write lands in freed memory, and it is the likely cause of the intermittent SIGSEGV in free() inside pthread_create() when a later worker reused a cached thread stack whose DTV had been overwritten. Three changes close it. Both workers now end the scan at their exit label instead of after the loop, so every way out ends it while the handler is still open; ending a scan that was never begun is harmless. ha_innobase::close() ends any worker or coordinator scan still open before row_prebuilt_free(), so a path that misses it is ended properly rather than left for the destructor; with the SQL layer put back as it was, this alone keeps ASAN clean. And ~Parallel_scan_worker() no longer calls end(), so parallel_scan_free(), now reached only for a handler destroyed without close(), cannot write through m_prebuilt either. The close() hook and the single call site are taken from a patch by a co-worker. Two debug assertions state the rule the defect broke, so it no longer takes ASAN to see it. ha_innobase::close() asserts that the SQL layer has already ended a worker's scan, and parallel_scan_free() asserts that no worker survives close(). The injection pwt_leave_scan_for_close makes the SQL layer leave the scan of a worker that holds no cursor -- one handed no chunk -- for close() to end, so the backstop is exercised on purpose and the first assertion is told to allow it. pwt_end_worker_scan() holds the injection and ends the scan at both exit labels. parallel_query_no_chunk (debug only) runs a full scan and a range scan of a one-page table with four workers, so three of them get no chunk, twice each, through the normal and the scan-only path, with and without the injection, checking the rows and that the workers ran every query. In a plain debug build, removing the exit-label calls fails the first assertion and removing the close() backstop fails the second. ~Parallel_scan_worker() no longer calling end() is defence for release builds and is not reachable while the backstop is there. This commit was prepared with Claude Code (Opus 5.5), which reproduced the pthread_create() crash under ASAN and traced it, wrote the fix, folded in the co-worker's close() hook and single call site, added the debug assertions, the injection and the test, confirmed in a plain debug build that the test fails with each of the two fixes removed, and ran the parallel_query tests in normal and --ps-protocol modes and the main suite with --parallel-worker-threads=4 under ASAN.