[MDEV-8554] Server crashes in base_list_iterator::next_fast on 1st execution of PS with a multi-table update Created: 2015-07-28  Updated: 2019-06-25  Resolved: 2015-08-03

Status: Closed
Project: MariaDB Server
Component/s: Prepared Statements
Affects Version/s: 5.5, 10.0, 10.1
Fix Version/s: 5.5.45

Type: Bug Priority: Major
Reporter: Elena Stepanova Assignee: Sergei Petrunia
Resolution: Fixed Votes: 0
Labels: None

Issue Links:
Relates
relates to MDEV-19844 (Reopen) Unexpected crash occured rep... Open

 Description   

Test case

CREATE TABLE t1 (a INT) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1),(2); # Not necessary, the table can be empty
 
CREATE TABLE t2 (b INT) ENGINE=MyISAM;
INSERT INTO t2 VALUES (3),(4); # Not necessary, the table can be empty
 
CREATE TABLE t3 (c INT) ENGINE=MyISAM;
INSERT INTO t3 VALUES (5),(6); # Not necessary, the table can be empty
 
CREATE OR REPLACE ALGORITHM=MERGE VIEW v3 AS SELECT * FROM t3;
 
PREPARE stmt FROM 'UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM t3 )';
UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM v3 );
EXECUTE stmt;

Stack trace from 5.5 commit e40bc659335f7f8b69427ed2d215c34c045a5ed7

#3  <signal handler called>
#4  0x0000000000591ccd in base_list_iterator::next_fast (this=0x7f78ebbb4070) at 5.5/sql/sql_list.h:455
#5  0x00000000005e9581 in List_iterator_fast<TABLE_LIST>::operator++ (this=0x7f78ebbb4070) at 5.5/sql/sql_list.h:569
#6  0x000000000062cf23 in st_select_lex::save_prep_leaf_tables (this=0x7f78eb144648, thd=0x7f78ec150060) at 5.5/sql/sql_lex.cc:4142
#7  0x000000000062cf78 in st_select_lex::save_prep_leaf_tables (this=0x7f78eb169810, thd=0x7f78ec150060) at 5.5/sql/sql_lex.cc:4152
#8  0x000000000062ce44 in LEX::save_prep_leaf_tables (this=0x7f78eb169080) at 5.5/sql/sql_lex.cc:4115
#9  0x00000000006eb182 in mysql_multi_update_prepare (thd=0x7f78ec150060) at 5.5/sql/sql_update.cc:1391
#10 0x0000000000636ef6 in mysql_execute_command (thd=0x7f78ec150060) at 5.5/sql/sql_parse.cc:2868
#11 0x0000000000656989 in Prepared_statement::execute (this=0x7f78eb134460, expanded_query=0x7f78ebbb4c90, open_cursor=false) at 5.5/sql/sql_prepare.cc:3928
#12 0x0000000000655aa0 in Prepared_statement::execute_loop (this=0x7f78eb134460, expanded_query=0x7f78ebbb4c90, open_cursor=false, packet=0x0, packet_end=0x0) at 5.5/sql/sql_prepare.cc:3587
#13 0x0000000000653bc4 in mysql_sql_stmt_execute (thd=0x7f78ec150060) at 5.5/sql/sql_prepare.cc:2737
#14 0x000000000063578c in mysql_execute_command (thd=0x7f78ec150060) at 5.5/sql/sql_parse.cc:2244
#15 0x000000000063f062 in mysql_parse (thd=0x7f78ec150060, rawbuf=0x7f78eb287078 "EXECUTE stmt", length=12, parser_state=0x7f78ebbb5620) at 5.5/sql/sql_parse.cc:5909
#16 0x0000000000632ca5 in dispatch_command (command=COM_QUERY, thd=0x7f78ec150060, packet=0x7f78ec207061 "EXECUTE stmt", packet_length=12) at 5.5/sql/sql_parse.cc:1079
#17 0x0000000000631e31 in do_command (thd=0x7f78ec150060) at 5.5/sql/sql_parse.cc:793
#18 0x0000000000734f49 in do_handle_one_connection (thd_arg=0x7f78ec150060) at 5.5/sql/sql_connect.cc:1269
#19 0x0000000000734cc3 in handle_one_connection (arg=0x7f78ec150060) at 5.5/sql/sql_connect.cc:1185
#20 0x0000000000b6de45 in pfs_spawn_thread (arg=0x7f78ec171ca0) at 5.5/storage/perfschema/pfs.cc:1015
#21 0x00007f78f229cb50 in start_thread (arg=<optimized out>) at pthread_create.c:304
#22 0x00007f78f055295d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112



 Comments   
Comment by Sergei Petrunia [ 2015-07-30 ]

valgrind shows it's reading memory that was allocated on statement's execution MEM_ROOT:

==9735== Invalid read of size 8
==9735==    at 0x5695D7: base_list_iterator::next_fast() (sql_list.h:455)
==9735==    by 0x5C3254: List_iterator_fast<TABLE_LIST>::operator++(int) (sql_list.h:569)
==9735==    by 0x607DAA: st_select_lex::save_prep_leaf_tables(THD*) (sql_lex.cc:4142)
==9735==    by 0x607DFF: st_select_lex::save_prep_leaf_tables(THD*) (sql_lex.cc:4152)
==9735==    by 0x607CCB: LEX::save_prep_leaf_tables() (sql_lex.cc:4115)
==9735==    by 0x6C8CC6: mysql_multi_update_prepare(THD*) (sql_update.cc:1391)
==9735==    by 0x612481: mysql_execute_command(THD*) (sql_parse.cc:2868)
==9735==    by 0x632196: Prepared_statement::execute(String*, bool) (sql_prepare.cc:3928)
==9735==    by 0x6312E3: Prepared_statement::execute_loop(String*, bool, unsigned char*, unsigned char*) (sql_prepare.cc:3587)
==9735==    by 0x62F42F: mysql_sql_stmt_execute(THD*) (sql_prepare.cc:2737)
==9735==    by 0x610D0F: mysql_execute_command(THD*) (sql_parse.cc:2244)
==9735==    by 0x61A64F: mysql_parse(THD*, char*, unsigned int, Parser_state*) (sql_parse.cc:5909)
==9735==    by 0x60E218: dispatch_command(enum_server_command, THD*, char*, unsigned int) (sql_parse.cc:1079)
==9735==    by 0x60D3A1: do_command(THD*) (sql_parse.cc:793)
==9735==    by 0x71597E: do_handle_one_connection(THD*) (sql_connect.cc:1269)
==9735==    by 0x7156F8: handle_one_connection (sql_connect.cc:1185)
==9735==  Address 0x88f6598 is 40 bytes inside a block of size 48 free'd
==9735==    at 0x4A0662E: free (vg_replace_malloc.c:366)
==9735==    by 0xCD4209: my_free (my_malloc.c:119)
==9735==    by 0xCC5B81: free_root (my_alloc.c:366)
==9735==    by 0x60F766: dispatch_command(enum_server_command, THD*, char*, unsigned int) (sql_parse.cc:1493)
==9735==    by 0x60D3A1: do_command(THD*) (sql_parse.cc:793)
==9735==    by 0x71597E: do_handle_one_connection(THD*) (sql_connect.cc:1269)
==9735==    by 0x7156F8: handle_one_connection (sql_connect.cc:1185)
==9735==    by 0xC165CA: pfs_spawn_thread (pfs.cc:1015)
==9735==    by 0x3509C07D8F: start_thread (pthread_create.c:309)
==9735==    by 0x35098F0F5C: clone (clone.S:115)
==9735== 

Comment by Sergei Petrunia [ 2015-07-30 ]

Discussed with sanja.

Rough idea of what happens:

PREPARE stmt FROM 'UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM t3 )';

For the subquery #2, select_lex->prep_leaf_list_state
changes from UNINIT to READY.

The query has thd->save_prep_leaf_list= FALSE, which
causes LEX::save_prep_leaf_tables() to do nothing.

UPDATE t1, t2 SET a = 1 WHERE a IN ( SELECT 0 FROM v3 );

This changes thd->save_prep_leaf_list from FALSE to TRUE.

This is done here: mysql_derived_merge:

 if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
     thd->lex->sql_command == SQLCOM_DELETE_MULTI)
   thd->save_prep_leaf_list= TRUE;

and the value is never restored back.

EXECUTE stmt;

Here, we attempt to use select_lex::leaf_tables and walk over freed memory.

The first suspect is save_prep_leaf_list. Why does UPDATE change it and never puts it back? This looks like a bug.

Comment by Sergei Petrunia [ 2015-07-30 ]

save_prep_leaf_list was introduced by 56eb6d7e69ecce856e2d54e2404157407cb7203b,

Committer: Igor Babaev <igor@askmonty.org> Tue Jun 14 05:03:03 2011

Fixed LP bug #794890.
Changed the code that processing of multi-updates and multi-deletes
with multitable views at the prepare stage.

Comment by Sergei Petrunia [ 2015-07-30 ]

http://lists.askmonty.org/pipermail/commits/2015-July/008211.html

Generated at Thu Feb 08 07:28:01 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.