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

MDEV-40038 tp_foreach() crashes on a dying transaction participant

Bug: tp_foreach() found an engine in hton2plugin[] and passed the
result of plugin_lock() to plugin_hton() without a check. While
reap_plugins() deinitializes an uninstalled engine (PLUGIN_IS_DYING),
the slot stays set until the end of ha_finalize_handlerton(), and
plugin_lock() returns NULL. The server crashed in plugin_hton(), for
example in RESET MASTER through ha_commit_checkpoint_request().

This is a regression from aed5928207, which changed plugin_foreach()
to tp_foreach() and lost the PLUGIN_IS_READY state mask.

Fix: add plugin_lock_ready(), which locks only a PLUGIN_IS_READY plugin
and reports under LOCK_plugin whether a failed lock was for a READY
plugin (out of memory in debug builds). tp_foreach() skips a plugin that
is not READY and returns an error for a READY plugin that cannot be
locked. An uninstalled but busy engine (PLUGIN_IS_DELETED) is not
visited, as with plugin_foreach() before.

The test uses a DEBUG_SYNC point in ha_finalize_handlerton() to run
RESET MASTER while an engine is deinitialized.

Assisted-by: https://mariadb.org/governance/governance-ai-policy/
This commit is contained in:
Jan Lindström
2026-08-26 06:43:18 +03:00
committed by Jan Lindström
parent dddbd5ff42
commit 3f0b82d2ba
6 changed files with 105 additions and 1 deletions

View File

@@ -1064,6 +1064,34 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr)
}
/**
Lock a plugin only if it is PLUGIN_IS_READY.
Like plugin_lock(), but returns NULL for a plugin that is still being
installed, is being deinitialized, or is already freed. The caller must
unlock a non-NULL result with plugin_unlock().
@param[out] failed Set when the plugin is READY but locking failed
(out of memory in debug builds). The state is read
under LOCK_plugin, so it matches the lock result.
A NULL result with *failed false means the plugin
is not READY.
*/
plugin_ref plugin_lock_ready(THD *thd, plugin_ref ptr, bool *failed)
{
LEX *lex= thd ? thd->lex : 0;
plugin_ref rc;
DBUG_ENTER("plugin_lock_ready");
mysql_mutex_lock(&LOCK_plugin);
plugin_ref_to_int(ptr)->locks_total++;
rc= intern_plugin_lock(lex, ptr, PLUGIN_IS_READY);
*failed= !rc && (plugin_ref_to_int(ptr)->state == PLUGIN_IS_READY);
mysql_mutex_unlock(&LOCK_plugin);
DBUG_RETURN(rc);
}
/*
Notes on lifetime: