Bug #70238 Memory leak in embedded server when executing prepared statements
Submitted: 4 Sep 2013 6:42 Modified: 6 Sep 2017 9:03
Reporter: Guangpu Feng Email Updates:
Status: Can't repeat Impact on me:
None 
Category:MySQL Server: Prepared statements Severity:S2 (Serious)
Version:5.6.11, 5.6.15 OS:Any
Assigned to: CPU Architecture:Any
Tags: embedded, libmysqld, memory leak

[4 Sep 2013 6:42] Guangpu Feng
Description:
The embedded server leaks memeory when executing perpared statements

part of the valgrind output:

==29639== 2,064 bytes in 1 blocks are definitely lost in loss record 3 of 4
==29639==    at 0x4A05FDE: malloc (vg_replace_malloc.c:236)
==29639==    by 0x4635E1: my_malloc (my_malloc.c:38)
==29639==    by 0x462C72: init_alloc_root (my_alloc.c:63)
==29639==    by 0x448935: mysql_stmt_init (libmysql.c:1512)
==29639==    by 0x444277: main (in /u01/mysql5611/bug62136)
==29639== 
==29639== 4,112 bytes in 1 blocks are definitely lost in loss record 4 of 4
==29639==    at 0x4A05FDE: malloc (vg_replace_malloc.c:236)
==29639==    by 0x4635E1: my_malloc (my_malloc.c:38)
==29639==    by 0x462C72: init_alloc_root (my_alloc.c:63)
==29639==    by 0x44894B: mysql_stmt_init (libmysql.c:1513)
==29639==    by 0x444277: main (in /u01/mysql5611/bug62136)
==29639== 
==29639== LEAK SUMMARY:
==29639==    definitely lost: 6,176 bytes in 2 blocks
==29639==    indirectly lost: 0 bytes in 0 blocks
==29639==      possibly lost: 0 bytes in 0 blocks
==29639==    still reachable: 568 bytes in 2 blocks
==29639==         suppressed: 0 bytes in 0 blocks
==29639== 
==29639== For counts of detected and suppressed errors, rerun with: -v
==29639== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 6 from 6)

the reason is that the assignments of stmt->mem_root in emb_read_prepare_result and stmt->result in emb_read_binary_rows without freeing the memory in MEM_ROOT.

libmysqd/lib_sql.cc

line 263:

 251   if (thd->first_data)
 252   {
 253     if (emb_read_query_result(mysql))
 254       return 1;
 255     stmt->field_count= mysql->field_count;
 256     mysql->status= MYSQL_STATUS_READY;
 257     res= thd->cur_data;
 258     thd->cur_data= NULL;
 259     if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
 260       mysql->server_status|= SERVER_STATUS_IN_TRANS;
 261 
 262     stmt->fields= mysql->fields;
 263     stmt->mem_root= res->alloc;
 264     mysql->fields= NULL;
 265     my_free(res);
 266   }

line 365:

 357 int emb_read_binary_rows(MYSQL_STMT *stmt)                                                                                                                         
 358 {
 359   MYSQL_DATA *data;
 360   if (!(data= emb_read_rows(stmt->mysql, 0, 0)))
 361   {
 362     set_stmt_errmsg(stmt, &stmt->mysql->net);
 363     return 1;
 364   }
 365   stmt->result= *data;
 366   my_free(data);
 367   set_stmt_errmsg(stmt, &stmt->mysql->net);
 368   return 0;
 369 }

How to repeat:
Test program will be attached, see the first comment.

Suggested fix:
--- libmysqld/lib_sql.cc        (revision 4360)
+++ libmysqld/lib_sql.cc        (working copy)
@@ -260,7 +260,6 @@
       mysql->server_status|= SERVER_STATUS_IN_TRANS;
 
     stmt->fields= mysql->fields;
-    stmt->mem_root= res->alloc;
     mysql->fields= NULL;
     my_free(res);
   }
@@ -362,6 +361,7 @@
     set_stmt_errmsg(stmt, &stmt->mysql->net);
     return 1;
   }
+  free_root(&(stmt->result.alloc), MYF(0));
   stmt->result= *data;
   my_free(data);
   set_stmt_errmsg(stmt, &stmt->mysql->net);

[No-Mysql@db031133.sqa.cm4 /u01/mysql_prj/PS5518/branches/mysql-5.6.11]
[4 Sep 2013 6:54] Guangpu Feng
libmysqld_prepare_statement.c

Attachment: libmysqld_prepared_statement.c (application/octet-stream, text), 3.20 KiB.

[4 Sep 2013 6:55] Guangpu Feng
contents of my_embedded.cnf:

[client]                                                                                                                                                                
socket = /u01/mysql5611/run/mysql.sock

[embedded]
sort_buffer_size=1M
core-file
#### Baes dir ####
basedir = /u01/mysql5611
datadir = /u01/mysql5611/data_embedded
tmpdir = /u01/mysql5611/tmp_embedded
socket = /u01/mysql5611/run_embedded/mysql.sock
#### Base configure info ####
port = 5611
skip-name-resolve
old_passwords = 0 
lower_case_table_names = 1 
open_files_limit = 65535
read_rnd_buffer_size = 5M
max_allowed_packet = 24M 
max_connect_errors = 500 
max_connections = 5 
max_user_connections = 4 
thread_cache_size=2
table_open_cache=16
table_definition_cache=8

server-id= 5611

#innodb
innodb_data_home_dir = /u01/mysql5611/data_embedded
default-storage-engine = INNODB
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1 
innodb_flush_log_at_trx_commit = 1 
innodb_lock_wait_timeout = 100 
innodb_additional_mem_pool_size = 4M
innodb_buffer_pool_size = 64M 
innodb_log_buffer_size= 8M
innodb_log_file_size = 16M 
innodb_log_files_in_group = 4 
innodb_max_dirty_pages_pct = 75

thread_stack = 1524288
[mysqld_safe]
pid-file = /u01/mysql5611/run_embedded/mysqld.pid
[4 Sep 2013 8:25] Guangpu Feng
in mysql_stmt_init
...
  init_alloc_root(&stmt->mem_root, 2048, 2048);    // line 1512
  init_alloc_root(&stmt->result.alloc, 4096, 4096);// line 1513
...

==29639== 2,064 bytes in 1 blocks are definitely lost in loss record 3 of 4
==29639==    at 0x4A05FDE: malloc (vg_replace_malloc.c:236)
==29639==    by 0x4635E1: my_malloc (my_malloc.c:38)
==29639==    by 0x462C72: init_alloc_root (my_alloc.c:63)
==29639==    by 0x448935: mysql_stmt_init (libmysql.c:1512)
==29639==    by 0x444277: main (in /u01/mysql5611/bug62136)

2064 bytes come from: 2048 + sizeof(USED_MEM)

==29639== 4,112 bytes in 1 blocks are definitely lost in loss record 4 of 4
==29639==    at 0x4A05FDE: malloc (vg_replace_malloc.c:236)
==29639==    by 0x4635E1: my_malloc (my_malloc.c:38)
==29639==    by 0x462C72: init_alloc_root (my_alloc.c:63)
==29639==    by 0x44894B: mysql_stmt_init (libmysql.c:1513)
==29639==    by 0x444277: main (in /u01/mysql5611/bug62136)

4112 bytes come from: 4096 + sizeof(USED_MEM)
[8 Jan 2014 10:05] MySQL Verification Team
Hello Guangpu,

Thank you for the bug report and test case.

Thanks,
Umesh
[6 Sep 2017 9:03] Manyi Lu
Posted by developer:
 
Embedded mode has already been removed starting from 8.0.