Skip to content

Commit b1d9dae

Browse files
committed
ASAN: heap-use-after-free with concurrent create/drop system trigger
In case there are 'on shutdown' triggers it could result in abnormal server termination if at the moment server shutdown is in progress the server received the DROP TRIGGER statement for one of 'ON SHUTDOWN' system triggers being already executed as part shutdown process. Another words, there is the race condition between running triggers on shutdown and execution of DROP TRIGGER for system triggers ON SHUTDOWN event. To fix the issue protect running on shutdown triggers and drop/create of system triggers under the lock to avoid race condition. Check under the new lock for the flag that shutdown is in progress and don't add/remove a trigger instance into/from internal array as part of handling CREATE/DROP TRIGGER for ON SHUTDOWN event. That is, add/drop metadata about the trigger but don't modify the internal runtime data structures.
1 parent 37dbfa4 commit b1d9dae

3 files changed

Lines changed: 101 additions & 3 deletions

File tree

sql/mysqld.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,6 +2046,7 @@ static void clean_up(bool print_message)
20462046
#ifndef EMBEDDED_LIBRARY
20472047
Events::deinit();
20482048
#endif
2049+
deinit_sys_triggers_environment();
20492050
my_free_open_file_info();
20502051
if (defaults_argv)
20512052
free_defaults(defaults_argv);
@@ -4572,6 +4573,8 @@ static int init_common_variables()
45724573
global_system_variables.auto_increment_offset;
45734574
#endif /* WITH_WSREP */
45744575

4576+
init_sys_triggers_environment();
4577+
45754578
return 0;
45764579
}
45774580

sql/sql_sys_or_ddl_trigger.cc

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,54 @@
3232

3333
static LEX_CSTRING event_table_name{STRING_WITH_LEN("event")};
3434
static bool sys_triggers_enabled= false;
35+
static bool run_on_shutdown_triggers= false;
36+
static mysql_mutex_t LOCK_firing_on_shutdown_triggers;
37+
static bool sys_triggers_env_inited= false;
38+
39+
#ifdef HAVE_PSI_INTERFACE
40+
41+
PSI_mutex_key key_LOCK_firing_on_shutdown_triggers;
42+
43+
static PSI_mutex_info all_sys_trg_mutexes[]=
44+
{
45+
{ &key_LOCK_firing_on_shutdown_triggers,
46+
"LOCK_on_firing_instance_level_triggers", PSI_FLAG_GLOBAL}
47+
};
48+
49+
static void init_sys_triggers_psi_keys(void)
50+
{
51+
const char* category= "sql";
52+
int count;
53+
54+
count= array_elements(all_sys_trg_mutexes);
55+
mysql_mutex_register(category, all_sys_trg_mutexes, count);
56+
}
57+
#endif
58+
59+
void init_sys_triggers_environment()
60+
{
61+
if (sys_triggers_env_inited)
62+
return;
63+
64+
#ifdef HAVE_PSI_INTERFACE
65+
init_sys_triggers_psi_keys();
66+
#endif
67+
mysql_mutex_init(key_LOCK_firing_on_shutdown_triggers,
68+
&LOCK_firing_on_shutdown_triggers,
69+
MY_MUTEX_INIT_FAST);
70+
sys_triggers_env_inited= true;
71+
}
72+
73+
74+
void deinit_sys_triggers_environment()
75+
{
76+
if (sys_triggers_env_inited)
77+
{
78+
mysql_mutex_destroy(&LOCK_firing_on_shutdown_triggers);
79+
sys_triggers_env_inited= false;
80+
}
81+
}
82+
3583

3684
/**
3785
Raise the error ER_TRG_ALREADY_EXISTS
@@ -456,6 +504,18 @@ static void register_system_triggers(Sys_trigger *sys_trg,
456504

457505
void unregister_trigger(sp_name *spname)
458506
{
507+
mysql_mutex_lock(&LOCK_firing_on_shutdown_triggers);
508+
if (run_on_shutdown_triggers)
509+
{
510+
/*
511+
Don't attempt to remove a system trigger from the sys_triggers array
512+
at the same time the shutdown is in progress since it could result in
513+
race condition on accessing the array from different threads
514+
*/
515+
mysql_mutex_unlock(&LOCK_firing_on_shutdown_triggers);
516+
return;
517+
}
518+
459519
for (int i= 0; i < TRG_ACTION_MAX; i++)
460520
{
461521
for (int j= 0; j < TRG_SYS_EVENT_MAX - TRG_EVENT_STARTUP; j++)
@@ -473,13 +533,15 @@ void unregister_trigger(sp_name *spname)
473533
sys_triggers[i][j]= sys_trg->next;
474534

475535
sys_trg->destroy();
536+
mysql_mutex_unlock(&LOCK_firing_on_shutdown_triggers);
476537
return;
477538
}
478539
prev_sys_trg= sys_trg;
479540
sys_trg= sys_trg->next;
480541
}
481542
}
482543
}
544+
mysql_mutex_unlock(&LOCK_firing_on_shutdown_triggers);
483545
}
484546

485547

@@ -625,12 +687,23 @@ bool mysql_create_sys_trigger(THD *thd)
625687

626688
events_mask = events_mask << 1;
627689

690+
mysql_mutex_lock(&LOCK_firing_on_shutdown_triggers);
691+
/*
692+
Check under the lock LOCK_firing_on_shutdown_triggers that
693+
shutdown is not in progress. Do it here and not in the function
694+
register_system_triggers, since thd_for_sys_triggers is destroyed
695+
on shutdown.
696+
*/
697+
if (run_on_shutdown_triggers)
698+
{
699+
mysql_mutex_unlock(&LOCK_firing_on_shutdown_triggers);
700+
my_ok(thd);
701+
return false;
702+
}
703+
628704
Sys_trigger *sys_trg=
629705
new (thd_for_sys_triggers->mem_root) Sys_trigger(thd_for_sys_triggers,
630706
thd->lex->sphead);
631-
register_system_triggers(
632-
sys_trg, thd->lex->trg_chistics.action_time,
633-
Event_parse_data::enum_kind(events_mask));
634707

635708
/*
636709
Stop destroy of sp_head for just handled CREATE TRIGGER statement
@@ -639,6 +712,13 @@ bool mysql_create_sys_trigger(THD *thd)
639712
lex_end_nops() -> sp_head::destroy
640713
*/
641714
thd->lex->sphead= nullptr;
715+
716+
register_system_triggers(
717+
sys_trg, thd->lex->trg_chistics.action_time,
718+
Event_parse_data::enum_kind(events_mask));
719+
720+
mysql_mutex_unlock(&LOCK_firing_on_shutdown_triggers);
721+
642722
my_ok(thd);
643723
return false;
644724
}
@@ -1566,6 +1646,14 @@ void run_before_shutdown_triggers(bool bootstrap_or_noacl)
15661646
if (bootstrap_or_noacl)
15671647
return;
15681648

1649+
mysql_mutex_lock(&LOCK_firing_on_shutdown_triggers);
1650+
/*
1651+
Set the flag to avoid adding/removing system triggers in
1652+
the sys_triggers array at the same moment as server shutdown
1653+
is in progress.
1654+
*/
1655+
run_on_shutdown_triggers= true;
1656+
15691657
bool stack_top;
15701658
init_thd_for_on_startup_shutdown_triggers(&stack_top);
15711659

@@ -1584,6 +1672,10 @@ void run_before_shutdown_triggers(bool bootstrap_or_noacl)
15841672
destroy_sys_triggers();
15851673
lex_end_nops(thd_for_sys_triggers->lex);
15861674
delete thd_for_sys_triggers;
1675+
thd_for_sys_triggers= nullptr;
1676+
1677+
mysql_mutex_unlock(&LOCK_firing_on_shutdown_triggers);
1678+
15871679
set_current_thd(original_thd);
15881680
}
15891681

sql/sql_sys_or_ddl_trigger.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,7 @@ void report_trg_already_exist_error(const sp_name *spname);
114114
bool fetch_trigger_record_by_name(TABLE *event_table, const sp_name *spname);
115115
bool find_sys_trigger_by_name(THD *thd, sp_name *spname);
116116

117+
void init_sys_triggers_environment();
118+
void deinit_sys_triggers_environment();
119+
117120
#endif /* SQL_SQL_SYS_OR_DDL_TRIGGER_H */

0 commit comments

Comments
 (0)