Bug #39427 join_nested.test produces valgrind warning in mysql_create_view()
Submitted: 12 Sep 2008 19:18 Modified: 14 Sep 2008 6:55
Reporter: Gleb Shchepa Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server: Views Severity:S1 (Critical)
Version:5.0+ OS:Any
Assigned to: CPU Architecture:Any
Tags: valgrind

[12 Sep 2008 19:18] Gleb Shchepa
Description:
On some builds join_nested.test produces valgrind warning:

==3414== Invalid read of size 1
==3414==    at 0x8249452: String::append(char const*, unsigned) (sql_string.cc:459)
==3414==    by 0x83E0ED5: mysql_create_view(THD*, TABLE_LIST*, enum_view_create_mode) (sql_view.cc:652)
==3414==    by 0x827089F: mysql_execute_command(THD*) (sql_parse.cc:4985)
==3414==    by 0x827168C: mysql_parse(THD*, char const*, unsigned, char const**) (sql_parse.cc:6185)
==3414==    by 0x8273E40: dispatch_command(enum_server_command, THD*, char*, unsigned) (sql_parse.cc:1874)
==3414==    by 0x827530E: do_command(THD*) (sql_parse.cc:1580)
==3414==    by 0x8276475: handle_one_connection (sql_parse.cc:1186)
==3414==    by 0x406F19A: start_thread (in /lib/libpthread-2.8.so)
==3414==    by 0x41B983D: clone (in /lib/libc-2.8.so)
==3414==  Address 0x4e99a73 is not stack'd, malloc'd or (recently) free'd

How to repeat:
No idea: it fails on my local build of mysql-5.0-bugteam tree.

OTOH the root cause of the warning is obvious:
mysql_create_view() calls mysql_register_view() and uses
stack data of mysql_register_view() outside of that function.

mysql_create_view() reads value of views->query.str at:

  buff.append(views->query.str, views->query.length);

views->query.str contains pointer to freed (stack) memory
of the mysql_register_view() function local variable buff
(or str, that is same):

static int mysql_register_view(THD *thd, TABLE_LIST *view,
                               enum_view_create_mode mode)
{
  LEX *lex= thd->lex;
  char buff[4096];
  String str(buff,(uint32) sizeof(buff), system_charset_info);
...
    lex->unit.print(&str);
...
  /* fill structure */
  view->query.str= str.c_ptr_safe();
  view->query.length= str.length();

Suggested fix:
Easiest way to fix that is to move buffer variable declaration
to the function that uses its value (from mysql_register_view()
to mysql_create_view()):

--- old/sql_view.cc	2008-09-13 00:06:32.000000000 +0500
+++ new/sql_view.cc	2008-09-13 00:06:27.000000000 +0500
@@ -27,7 +27,7 @@
 const LEX_STRING view_type= { (char*) STRING_WITH_LEN("VIEW") };
 
 static int mysql_register_view(THD *thd, TABLE_LIST *view,
-			       enum_view_create_mode mode);
+			       enum_view_create_mode mode, String &str);
 
 const char *updatable_views_with_limit_names[]= { "NO", "YES", NullS };
 TYPELIB updatable_views_with_limit_typelib=
@@ -393,6 +393,8 @@
 #endif
   SELECT_LEX_UNIT *unit= &lex->unit;
   bool res= FALSE;
+  char buff[4096];
+  String str(buff, (uint32) sizeof(buff), system_charset_info);
   DBUG_ENTER("mysql_create_view");
 
   /* This is ensured in the parser. */
@@ -610,7 +612,7 @@
     goto err;
   }
   VOID(pthread_mutex_lock(&LOCK_open));
-  res= mysql_register_view(thd, view, mode);
+  res= mysql_register_view(thd, view, mode, str);
 
   if (mysql_bin_log.is_open())
   {
@@ -743,6 +745,7 @@
     thd		- thread handler
     view	- view description
     mode	- VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE
+    str         - buffer to keep short-living view->query.str value
 
   RETURN
      0	OK
@@ -751,11 +754,9 @@
 */
 
 static int mysql_register_view(THD *thd, TABLE_LIST *view,
-			       enum_view_create_mode mode)
+			       enum_view_create_mode mode, String &str)
 {
   LEX *lex= thd->lex;
-  char buff[4096];
-  String str(buff,(uint32) sizeof(buff), system_charset_info);
   char md5[MD5_BUFF_LENGTH];
   bool can_be_merged;
   char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
[13 Sep 2008 22:13] MySQL Verification Team
seen bug #39040 ?
[14 Sep 2008 6:55] Gleb Shchepa
> [14 Sep 0:13] Shane Bester
> seen bug #39040 ?

Yes, this is duplicated report, thank you.