MDEV-38747: ASAN errors in Optimizer_hint_parser::Identifier::to_ide…#4622
MDEV-38747: ASAN errors in Optimizer_hint_parser::Identifier::to_ide…#4622DaveGosselin-MariaDB wants to merge 1 commit into12.2from
Conversation
1c5abb3 to
e52f7ff
Compare
|
What I do not like about this fix. So after the fix we have: int sp_lex_keeper::validate_lex_and_exec_core(THD *thd, uint *nextp,
bool open_tables,
sp_lex_instr* instr)
{
...
while (true)
{
String sql_query;
if (instr->is_invalid() || m_lex->needs_reprepare)
{
...
LEX *lex= instr->parse_expr(thd, thd->spcont->m_sp, m_lex, &sql_query);
...
}
bool rc= reset_lex_and_exec_core(thd, nextp, open_tables, instr,
rerun_the_same_instr);
if (!rc)
break;
...
instr->invalidate();
}
}Consider this siutation: We enter this function and Note that the parsed statement remains available for further execution. If/when we enter We'll have statement execute while hints data structures refer to free'd memory. |
…t_cli Summary: A trigger specifying a hint where the hint has a query block name will cause an ASAN failure because hint resolution occurs after query parsing, not during query parsing. The trigger execution logic uses a stack-local string to hold the query and hint text during parsing. During trigger execution, query parsing and query execution happen in different function contexts, so the query string used during parsing goes out of scope, freeing its memory. But as hint resolution occurs after parsing is complete (and hints merely point into the query string, they don't copy from it), the hints refer into a deallocated query string upon hint resolution. Details: Prior to the commit introducing this bug, hint resolution was done via a call to `LEX::resolve_optimizer_hints_in_last_select` when parsing the `query_specification:` grammar rule. This meant that any string containing the query (and hints) was in scope for the entire lifetime of query parsing and hint resolution. In the patch introducing this bug, `resolve_optimizer_hints_in_last_select` was replaced with `handle_parsed_optimizer_hints_in_last_select`, changing the parsing such that it merely cached hints for resolution during query execution. Later, after parsing ends and upon query execution, `mysql_execute_command` calls `LEX::resolve_optimizer_hints` to resolve hints. When executing a typical SQL command trigger, `sp_lex_instr::parse_expr` reparses the query associated with the trigger and does so using a stack-local String variable to hold the query text. `sp_lex_instr::parse_expr` returns after query parsing completes but before hint resolution begins. Since the string holding the query was stack-local in `sp_lex_instr::parse_expr` and destroyed when the method returned, the query string (and hints with it) were deallocated, leading to the ASAN failure on hint resolution. When executing the trigger, `sp_instr_stmt::exec_core` calls `mysql_execute_command` which calls `LEX::resolve_optimizer_hints` to complete hint resolution but the query string that the hints depends on no longer exists at this point. As noted, the stack-local `query_string` variable in `sp_lex_inst::parse_expr` goes out-of-scope and is freed when the `sp_lex_instr::parse_expr` returns. In contrast, in the general case, when a `COM_QUERY` is processed during `dispatch_command`, the query string lives on the `THD` for the lifetime of the query independent of some particular function's scope. For triggers, the necessary lifetime of that query string needs to be as long as `sp_lex_keeper::validate_lex_and_exec_core` which covers both the query string parsing via `sp_lex_instr::parse_expr` and the procedure's execution during `reset_lex_and_exec_core`. Consequently, this patch lifts the `query_string` buffer up out of `parse_expr` and onto the `sp_lex_instr` itself which guarantees that its lifetime is as long as the instruction, which also guarantees the query string's lifetime extends across parsing and execution, including hint resolution. This also covers any cases where the trigger is successfully executed consecutive times but not reparsed between those executions. QB_NAME is not the only affected hint kind; hints with some query block identifier text for the query block, like ``` NO_MERGE(`@select#1`) ``` will also cause the crash while `NO_MERGE()` will not.
e52f7ff to
f40ebc0
Compare
…nt_cli
Summary:
A trigger specifying a hint where the hint has a query block name will cause
an ASAN failure because hint resolution occurs after query parsing, not
during query parsing. The trigger execution logic uses a stack-local
string to hold the query and hint text during parsing. During trigger
execution, query parsing and query execution happen in different function
contexts, so the query string used during parsing goes out of scope, freeing
its memory. But as hint resolution occurs after parsing is complete (and
hints merely point into the query string, they don't copy from it), the hints
refer into a deallocated query string upon hint resolution.
Details:
Prior to the commit introducing this bug, hint resolution was done via a call
to
LEX::resolve_optimizer_hints_in_last_selectwhen parsing thequery_specification:grammar rule. This meant that any string containingthe query (and hints) was in scope for the entire lifetime of query parsing
and hint resolution.
In the patch introducing this bug,
resolve_optimizer_hints_in_last_selectwas replaced with
handle_parsed_optimizer_hints_in_last_select, changingthe parsing such that it merely cached hints for resolution during query
execution. Later, after parsing ends and upon query execution,
mysql_execute_commandcallsLEX::resolve_optimizer_hintsto resolve hints.When executing a typical SQL command trigger,
sp_lex_instr::parse_exprreparses the query associated with the trigger and does so using a stack-local
String variable to hold the query text.
sp_lex_instr::parse_exprreturns afterquery parsing completes but before hint resolution begins. Since
the string holding the query was stack-local in
sp_lex_instr::parse_expranddestroyed when the method returned, the query string (and hints with it) were
deallocated, leading to the ASAN failure on hint resolution. When executing
the trigger,
sp_instr_stmt::exec_corecallsmysql_execute_commandwhichcalls
LEX::resolve_optimizer_hintsto complete hint resolution but the querystring that the hints depends on no longer exists at this point.
As noted, the stack-local
query_stringvariable insp_lex_inst::parse_exprgoes out-of-scope and is freed when the
sp_lex_instr::parse_exprreturns. Incontrast, in the general case, when a
COM_QUERYis processed duringdispatch_command, the query string lives on theTHDfor the lifetime ofthe query independent of some particular function's scope.
For triggers, the necessary lifetime of that query string needs to be as long
as
sp_lex_keeper::validate_lex_and_exec_corewhich covers both the querystring parsing via
sp_lex_instr::parse_exprand the procedure's executionduring
reset_lex_and_exec_core. Consequently, this patch lifts thequery_stringbuffer up out ofparse_exprand onto thesp_lex_instritselfwhich guarantees that its lifetime is as long as the instruction, which also
guarantees the query string's lifetime extends across parsing and execution,
including hint resolution. This also covers any cases where the trigger is
successfully executed consecutive times but not reparsed between those
executions.
QB_NAME is not the only affected hint kind; hints with some query block
identifier text for the query block, like
will also cause the crash while
NO_MERGE()will not.