1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-40004: Server crashes in sp_head::register_instr_mem_root_for_deallocation upon shutdown

n shutdown server could crash in case triggers executed during server run
and some of triggers instructions were re-compiled.

The crash is caused by attempt to allocate a memory for storing pointers
on mem_roots used for memory allocation taken place on re-parsing failing
trigger's statements. The reason of crash is dereferencing of nullptr
returning by the function current_thd().

To fix the issue, use dummy THD on shutdown the table definition cache.
tdc_start_shutdown() is solely invoked from the function clean_up()
but the later is called from many places around the source code, not only
from mysql_main(), particularly clean_up() is called from unireg_abort().
Therefore, the extra argument added into the signature of the function
cleanup() to allow explicit request of use dummy THD on shutdown the table
definition cache.
This commit is contained in:
Dmitry Shulga
2026-06-18 16:13:33 +07:00
parent c8f382faa7
commit 0cb2cc959e
5 changed files with 89 additions and 6 deletions

View File

@@ -2122,6 +2122,23 @@ i
2
1
1
DROP TEMPORARY TABLE t1;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
SET sql_mode=@save_sql_mode;
#
# MDEV-40004: Server crashes in sp_head::register_instr_mem_root_for_deallocation upon shutdown
#
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2);
CREATE TRIGGER tr BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM v1;
CREATE VIEW v1 AS SELECT 0 AS a;
UPDATE t1 SET a = 3;
ERROR HY000: The target table v1 of the DELETE is not updatable
CREATE OR REPLACE VIEW v1 AS SELECT 0 AS a;
UPDATE t1 SET a = 4;
ERROR HY000: The target table v1 of the DELETE is not updatable
# restart
# Clean up
DROP VIEW v1;
DROP TABLE t1;

View File

@@ -2923,9 +2923,33 @@ $
--delimiter ;
CALL pkg1.p2();
DROP TEMPORARY TABLE t1;
DROP PACKAGE BODY pkg1;
DROP PACKAGE pkg1;
SET sql_mode=@save_sql_mode;
--echo #
--echo # MDEV-40004: Server crashes in sp_head::register_instr_mem_root_for_deallocation upon shutdown
--echo #
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1),(2);
CREATE TRIGGER tr BEFORE UPDATE ON t1 FOR EACH ROW DELETE FROM v1;
CREATE VIEW v1 AS SELECT 0 AS a;
--error ER_NON_UPDATABLE_TABLE
UPDATE t1 SET a = 3;
CREATE OR REPLACE VIEW v1 AS SELECT 0 AS a;
--error ER_NON_UPDATABLE_TABLE
UPDATE t1 SET a = 4;
--source include/restart_mysqld.inc
--echo # Clean up
DROP VIEW v1;
DROP TABLE t1;
--enable_ps2_protocol

View File

@@ -1565,7 +1565,7 @@ void handle_connections_sockets();
static bool read_init_file(char *file_name);
pthread_handler_t handle_slave(void *arg);
static void clean_up(bool print_message);
static void clean_up(bool print_message, bool use_dummy_thd= false);
static int test_if_case_insensitive(const char *dir_name);
#ifndef EMBEDDED_LIBRARY
@@ -1974,7 +1974,7 @@ static void mysqld_exit(int exit_code)
#endif /* !EMBEDDED_LIBRARY */
static void clean_up(bool print_message)
static void clean_up(bool print_message, bool use_dummy_thd)
{
DBUG_PRINT("exit",("clean_up"));
if (cleanup_done++)
@@ -2009,7 +2009,7 @@ static void clean_up(bool print_message)
lex_free(); /* Free some memory */
item_create_cleanup();
cleanup_json_schema_keyword_hash();
tdc_start_shutdown();
tdc_start_shutdown(use_dummy_thd);
#ifdef HAVE_REPLICATION
semi_sync_master_deinit();
#endif
@@ -6164,7 +6164,7 @@ int mysqld_main(int argc, char **argv)
close_connections();
ha_pre_shutdown();
clean_up(1);
clean_up(1, true);
sd_notify(0, "STATUS=MariaDB server is down");
/* (void) pthread_attr_destroy(&connection_attrib); */

View File

@@ -632,7 +632,7 @@ bool tdc_init(void)
minimal in order to reduce number of references to pluggable engines.
*/
void tdc_start_shutdown(void)
void tdc_start_shutdown(bool use_dummy_thd)
{
DBUG_ENTER("tdc_start_shutdown");
if (tdc_inited)
@@ -645,8 +645,50 @@ void tdc_start_shutdown(void)
*/
tdc_size= 0;
tc_size= 0;
THD *thd= nullptr;
bool reset_back_current_thd= false;
if (use_dummy_thd && _current_thd() == nullptr)
{
/*
There is no active THD, use the surrogate one to handle memory
allocations in Sql_alloc::operator new() that uses current_thd()
for getting THD to get access to memory allocation API
*/
thd= new THD(0);
DBUG_ASSERT(thd != nullptr);
thd->store_globals();
thd->init();
thd->set_query_inner((char*) STRING_WITH_LEN("intern:tdc_start_shutdown"),
default_charset_info);
reset_back_current_thd= true;
}
/* Free all cached but unused TABLEs and TABLE_SHAREs. */
/*
purge_tables() iterates along opened tables close every one of them.
In case tables use triggers it results in destroying sp_head objects
for every associated trigger. Destroying an object of the sp_head class
leads to deleting sp_instr objects. In case some of SP instruction
were previously re-parsed by the reason of changes in tables metadata,
the method
sp_head::register_instr_mem_root_for_deallocation()
is called to add a memory root used for re-parsing to the list of
mem_roots to be freed on sp_head destruction. Mem_roots for later
destruction are placed on the List that use current_thd() for memory
allocation. That is the reason why the dummy THD is created before
in case there is no active THD at the moment the function
tdc_start_shutdown is invoked.
*/
purge_tables();
if (reset_back_current_thd)
{
thd->reset_globals();
delete thd;
}
}
DBUG_VOID_RETURN;
}

View File

@@ -66,7 +66,7 @@ extern ulong tc_size;
extern uint32 tc_instances;
extern bool tdc_init(void);
extern void tdc_start_shutdown(void);
extern void tdc_start_shutdown(bool use_dummy_thd);
extern void tdc_deinit(void);
extern ulong tdc_records(void);
extern void tdc_purge(bool all);