Bug #76058 refactored parser code may ignore OOM errors and dereference NULL pointers
Submitted: 26 Feb 2015 9:45 Modified: 27 Mar 2015 2:18
Reporter: Gleb Shchepa Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Parser Severity:S2 (Serious)
Version:5.7.6 OS:Any
Assigned to: CPU Architecture:Any

[26 Feb 2015 9:45] Gleb Shchepa
Description:
When we build the parse tree, it's ok to defer OOM error processing in the most of cases:

rule:
  foo ...
  {
    $$= NEW_PTN PT_foo(...);
    /* check for NULL in $$ is optional here */
  }

In this case we don't care about NULL pointers inside the resulting parse tree: there must be a check for OOM issues at the top-lever grammar rule of any refactored statement.

However, after WL#7201, WL#7202 and WL#8062 the topmost grammar rules for INSERT, REPLACE, UPDATE and DELETE statements dereference parse tree pointer without any check for OOM or NULL pointer:

        | insert_stmt      { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
        | replace_stmt     { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
        | update_stmt      { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
        | delete_stmt      { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }

How to repeat:
Not feasible to repeat.

Suggested fix:
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 9998e3d..273f0c1 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -152,6 +152,20 @@ int yylex(void *yylval, void *yythd);
       MYSQL_YYABORT;                                   \
   } while(0)
 
+/**
+  PT_statement::make_cmd() wrapper to raise postponed error message on OOM
+
+  @note x may be NULL because of OOM error.
+*/
+#define MAKE_CMD(x)                                     \
+  do                                                    \
+  {                                                     \
+    if (YYTHD->is_error())                              \
+      MYSQL_YYABORT;                                    \
+    Lex->m_sql_cmd= (x)->make_cmd(YYTHD);               \
+  } while(0)
+
+
 #ifndef DBUG_OFF
 #define YYDEBUG 1
 #else
@@ -1622,7 +1636,7 @@ statement:
         | commit
         | create
         | deallocate
-        | delete_stmt           {  Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
+        | delete_stmt           {  MAKE_CMD($1); }
         | describe
         | do
         | drop
@@ -1633,7 +1647,7 @@ statement:
         | grant
         | handler
         | help
-        | insert_stmt           { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
+        | insert_stmt           { MAKE_CMD($1); }
         | install
         | kill
         | load
@@ -1648,7 +1662,7 @@ statement:
         | release
         | rename
         | repair
-        | replace_stmt          { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
+        | replace_stmt          { MAKE_CMD($1); }
         | reset
         | resignal_stmt
         | revoke
@@ -1663,7 +1677,7 @@ statement:
         | truncate
         | uninstall
         | unlock
-        | update_stmt           { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
+        | update_stmt           { MAKE_CMD($1); }
         | use
         | xa
         ;
@@ -12089,10 +12103,10 @@ describe:
 
 explainable_command:
           select  { CONTEXTUALIZE($1); }
-        | insert_stmt      { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
-        | replace_stmt     { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
-        | update_stmt      { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
-        | delete_stmt      { Lex->m_sql_cmd= $1->make_cmd(YYTHD); }
+        | insert_stmt                           { MAKE_CMD($1); }
+        | replace_stmt                          { MAKE_CMD($1); }
+        | update_stmt                           { MAKE_CMD($1); }
+        | delete_stmt                           { MAKE_CMD($1); }
         | FOR_SYM CONNECTION_SYM real_ulong_num
           {
             Lex->sql_command= SQLCOM_EXPLAIN_OTHER;
[27 Mar 2015 2:18] Paul DuBois
Noted in 5.7.7, 5.8.0 changelogs.

For table-modifying statements, the parser could dereference the 
parse tree without checking for out-of-memory conditions or null
pointers.