Skip to content

Commit c10ae04

Browse files
Dyre Tjeldvolldahlerlend
authored andcommitted
Bug#36655831: Segfault ALTER EVENT query executed using MLE sql-callout regular statement
Problem: Executing EVENT statement without an event body inside MLE SP caused segv. Root cause: Recursive stored programs are not allowed. This is enforced by the parser when it checks if thd->lex->sphead is already set before parsing another SP. Events are a type of SP which also uses thd->lex->sphead so this restriction also means that a stored procedure cannot contain event statements. The Regular_statement_handle interface adds an extra twist to this because each normal statement is executed internally using a prepared statement. Normally, this would break the check for recursive SPs, since the prepared statement has its own lex, and its sphead is not set (is nullptr). But since these internal prepared statements have their m_lex->sphead set to refrence the sp_head of the enclosing SP it still works and we get the expected error. WL#16298 allowes event statements to be prepared, and when the parser is invoked it assigns the sp_head for the event body to PS->m_lex->sphead, and as mentioned above, this means that the prepared event statement can appear inside an SP, without triggering the recursion error. The code for wl#16298 also made the assumption that it was ok to unconditionally assign thd->lex->sphead (actually PS->m_lex->sphead) to Event_parse_data::event_body, since either A) the event statement does not have a body and then thd->lex->sphead (PS->m_lex->sphead) is nullptr or, B) the event statement has an event body and then thd->lex->sphead (PS->m_lex->sphead) will have been assigned by the parser. However, for the internal prepared statement of an MLE SP A) does not hold and the result was that an incorrect reference to the MLE SP's sp_head was placed inside Event_parse_data::even_body, which would then be destroyed when the prepared statement was deallocated, leaving dangling pointers to it, which in turn caused this bug. Solution: Check Event_parse_data::body_changed, which is set by the parser after parsing an event body, before assuming that LEX::sphead is referencing an event body. This ensures that the destruction of Event_parse_data does not deallocate sp_head for the MLE procedure itself. Change-Id: I5948eab8499de6d1cfd2d88b08728c2733bcd3c0
1 parent b49f1af commit c10ae04

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

sql/event_db_repository.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ bool Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
125125
const LEX_CSTRING *new_name) {
126126
DBUG_TRACE;
127127
sp_head *sp = parse_data->event_body;
128-
assert(thd->lex->sphead == nullptr || thd->lex->sphead == sp);
128+
129+
// If sphead is non-null and the statement had an event body sphead must
130+
// reference the event body.
131+
assert(thd->lex->sphead == nullptr || !parse_data->body_changed ||
132+
thd->lex->sphead == sp);
129133

130134
/* None or both must be set */
131135
assert((new_dbname && new_name) || new_dbname == new_name);

sql/event_parse_data.cc

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,11 @@ struct Sql_cmd_event : public Sql_cmd_event_base {
591591
// not be an event body sp_head if this statement is part of an SP. This
592592
// also ensures that it is correctly cleaned up by lex_end() at the end of
593593
// the statement, so that no extra cleanup is required here.
594-
event_parse_data.event_body = thd->lex->sphead;
594+
if (event_parse_data.body_changed) {
595+
// When body_changed is true the parser has completed parsing an event
596+
// body and updated thd->lex->sphead to refer to it.
597+
event_parse_data.event_body = thd->lex->sphead;
598+
}
595599
} else {
596600
assert(thd->lex->sphead == nullptr);
597601
}
@@ -639,11 +643,16 @@ struct Sql_cmd_event : public Sql_cmd_event_base {
639643

640644
bool prepare(THD *thd) override {
641645
if constexpr (SQLCOM != SQLCOM_DROP_EVENT) {
642-
event_parse_data.event_body = thd->lex->sphead;
643-
// Prevents lex_end() from destroying event body sp_head
644-
// so that it can be destroyed by ~Prepared_statement()
645-
// instead.
646-
thd->lex->sphead = nullptr;
646+
if (event_parse_data.body_changed) {
647+
// When body_changed is true the parser has completed parsing an event
648+
// body and updated thd->lex->sphead to refer to it.
649+
event_parse_data.event_body = thd->lex->sphead;
650+
651+
// Prevents lex_end() from destroying event body sp_head
652+
// so that it can be destroyed by ~Prepared_statement()
653+
// instead.
654+
thd->lex->sphead = nullptr;
655+
}
647656
Prepared_stmt_arena_holder ps_arena_holder{thd};
648657

649658
// Event schedule expression cannot contain subqueries or stored function

0 commit comments

Comments
 (0)