summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2023-05-15 12:41:31 +0400
committerAlexander Barkov <bar@mariadb.com>2023-05-15 13:55:52 +0400
commitcb334ae07f5286198b9d5d15eb4f908cac5af7fc (patch)
tree06e3febc4f71fefd253aff78bcab6875b30b0d6c
parentc9eff1a144ba44846373660a30d342d3f0dc91a5 (diff)
downloadmariadb-git-bb-10.5-bar-MDEV-30680.tar.gz
MDEV-30680 Warning: Memory not freed: 280 on mangled query, LeakSanitizer: detected memory leaksbb-10.5-bar-MDEV-30680
The parser works as follows: The rule expr_lex returns a pointer to a newly created sp_expr_lex instance which is not linked to any MariaDB structures yet - it is pointed only from a Bison stack variable. The sp_expr_lex instance gets linked to other structures (such as sp_instr_jump_if_not) later, after scanning some following grammar. Problem before the fix: If a parse error happened immediately after expr_lex (before it got linked), the created sp_expr_lex value got lost causing a memory leak. Fix: - Using Bison's "destructor" directive to free the results of expr_lex on parse/oom errors. - Moving the call for LEX::cleanup_lex_after_parse_error() from MYSQL_YYABORT and yyerror inside parse_sql(). This is needed because Bison calls destructors after yyerror(), while it's important to delete the sp_expr_lex instance before LEX::cleanup_lex_after_parse_error(). The latter frees the memory root containing the sp_expr_lex instance. After this change the code block are executed in the following order: - yyerror() -- now only raises the error to DA (no cleanup done any more) - %destructor { delete $$; } <expr_lex> -- destructs the sp_expr_lex instance - LEX::cleanup_lex_after_parse_error() -- frees the memory root containing the sp_expr_lex instance
-rw-r--r--mysql-test/main/sp-memory-leak.result21
-rw-r--r--mysql-test/main/sp-memory-leak.test29
-rw-r--r--sql/sql_parse.cc11
-rw-r--r--sql/sql_yacc.yy21
4 files changed, 74 insertions, 8 deletions
diff --git a/mysql-test/main/sp-memory-leak.result b/mysql-test/main/sp-memory-leak.result
new file mode 100644
index 00000000000..aea278801d8
--- /dev/null
+++ b/mysql-test/main/sp-memory-leak.result
@@ -0,0 +1,21 @@
+#
+# MDEV-30680 Warning: Memory not freed: 280 on mangled query, LeakSanitizer: detected memory leaks
+#
+BEGIN NOT ATOMIC
+IF SCALAR() expected_THEN_here;
+END
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'expected_THEN_here;
+END' at line 2
+BEGIN NOT ATOMIC
+WHILE SCALAR() expected_DO_here;
+END
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'expected_DO_here;
+END' at line 2
+BEGIN NOT ATOMIC
+REPEAT SELECT 1; UNTIL SCALAR() expected_END_here;
+END
+$$
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'expected_END_here;
+END' at line 2
diff --git a/mysql-test/main/sp-memory-leak.test b/mysql-test/main/sp-memory-leak.test
new file mode 100644
index 00000000000..5b346aa8b10
--- /dev/null
+++ b/mysql-test/main/sp-memory-leak.test
@@ -0,0 +1,29 @@
+--echo #
+--echo # MDEV-30680 Warning: Memory not freed: 280 on mangled query, LeakSanitizer: detected memory leaks
+--echo #
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+BEGIN NOT ATOMIC
+ IF SCALAR() expected_THEN_here;
+END
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+BEGIN NOT ATOMIC
+ WHILE SCALAR() expected_DO_here;
+END
+$$
+DELIMITER ;$$
+
+
+DELIMITER $$;
+--error ER_PARSE_ERROR
+BEGIN NOT ATOMIC
+ REPEAT SELECT 1; UNTIL SCALAR() expected_END_here;
+END
+$$
+DELIMITER ;$$
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 4c7313265e8..623de4479ae 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -10496,6 +10496,17 @@ bool parse_sql(THD *thd, Parser_state *parser_state,
((thd->variables.sql_mode & MODE_ORACLE) ?
ORAparse(thd) :
MYSQLparse(thd)) != 0;
+
+ if (mysql_parse_status)
+ {
+ /*
+ Restore the original LEX if it was replaced when parsing
+ a stored procedure. We must ensure that a parsing error
+ does not leave any side effects in the THD.
+ */
+ LEX::cleanup_lex_after_parse_error(thd);
+ }
+
DBUG_ASSERT(opt_bootstrap || mysql_parse_status ||
thd->lex->select_stack_top == 0);
thd->lex->current_select= thd->lex->first_select_lex();
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a5a39228776..34120eb3e32 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -98,7 +98,6 @@ int yylex(void *yylval, void *yythd);
#define MYSQL_YYABORT \
do \
{ \
- LEX::cleanup_lex_after_parse_error(thd); \
YYABORT; \
} while (0)
@@ -149,13 +148,6 @@ static Item* escape(THD *thd)
static void yyerror(THD *thd, const char *s)
{
- /*
- Restore the original LEX if it was replaced when parsing
- a stored procedure. We must ensure that a parsing error
- does not leave any side effects in the THD.
- */
- LEX::cleanup_lex_after_parse_error(thd);
-
/* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */
if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0)
s= ER_THD(thd, ER_SYNTAX_ERROR);
@@ -1521,6 +1513,19 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <expr_lex>
expr_lex
+%destructor
+{
+ /*
+ In case of a syntax/oom error let's free the sp_expr_lex
+ instance, but only if it has not been linked to any structures
+ such as sp_instr_jump_if_not::m_lex_keeper yet, e.g.:
+ IF f1() THEN1
+ i.e. THEN1 came instead of the expected THEN causing a syntax error.
+ */
+ if (!$$->sp_lex_in_use)
+ delete $$;
+} <expr_lex>
+
%type <assignment_lex>
assignment_source_lex
assignment_source_expr