[MDEV-31102] Crash when pushing condition into view defined as union Created: 2023-04-21  Updated: 2023-05-25  Resolved: 2023-04-24

Status: Closed
Project: MariaDB Server
Component/s: Optimizer
Affects Version/s: 10.4, 10.5, 10.6
Fix Version/s: 10.11.3, 11.0.2, 10.4.29, 10.5.20, 10.6.13, 10.8.8, 10.9.6, 10.10.4

Type: Bug Priority: Blocker
Reporter: Igor Babaev Assignee: Igor Babaev
Resolution: Fixed Votes: 1
Labels: None

Issue Links:
Blocks

 Description   

This test case cases a crash of the server:

create table t1 (
   n int,
   lv varchar(31) charset latin1,
   mv varchar(31) charset utf8mb3
) engine=myisam;
insert into t1 values (1,'aa','xxx'), ('2','bb','yyy'), (3,'cc','zzz');
create view v1 as
select case when n=1 then lv when n=2 then mv else NULL end as r from t1 
union
select 'a'; 
 
prepare stmt from "select * from v1 where r < 'x'";
execute stmt;

sql/signal_handler.cc:238(handle_fatal_signal)[0x5619d7043b23]
/lib64/libpthread.so.0(+0xf890)[0x7f08d7e88890]
sql/item.cc:3411(Item_field::used_tables() const)[0x5619d706f433]
sql/item.h:5171(Used_tables_and_const_cache::used_tables_and_const_cache_join(Item const*))[0x5619d6c63f7d]
sql/item.h:5178(Used_tables_and_const_cache::used_tables_and_const_cache_update_and_join(Item*))[0x5619d6c64002]
sql/item.h:5187(Used_tables_and_const_cache::used_tables_and_const_cache_update_and_join(unsigned int, Item**))[0x5619d6c64044]
sql/item_func.h:161(Item_func::update_used_tables())[0x5619d6c656c0]
sql/item.h:7677(Item_direct_ref_to_item::update_used_tables())[0x5619d708c6eb]
sql/item.h:5177(Used_tables_and_const_cache::used_tables_and_const_cache_update_and_join(Item*))[0x5619d6c63fef]
sql/item.h:5187(Used_tables_and_const_cache::used_tables_and_const_cache_update_and_join(unsigned int, Item**))[0x5619d6c64044]
sql/item_func.h:161(Item_func::update_used_tables())[0x5619d6c656c0]
sql/sql_lex.cc:4670(st_select_lex::update_used_tables())[0x5619d6cc64b1]
sql/sql_select.cc:1927(JOIN::optimize_inner())[0x5619d6d44b40]
sql/sql_select.cc:1690(JOIN::optimize())[0x5619d6d43f6a]
sql/sql_union.cc:1490(st_select_lex_unit::optimize())[0x5619d6e0f9b2]
sql/sql_derived.cc:995(mysql_derived_optimize(THD*, LEX*, TABLE_LIST*))[0x5619d6c9f894]
sql/sql_derived.cc:200(mysql_handle_single_derived(LEX*, TABLE_LIST*, unsigned int))[0x5619d6c9d4c4]
sql/sql_select.cc:2180(JOIN::optimize_inner())[0x5619d6d45cd9]
sql/sql_select.cc:1690(JOIN::optimize())[0x5619d6d43f6a]
sql/sql_select.cc:4797(mysql_select(THD*, TABLE_LIST*, unsigned int, List<Item>&, Item*, unsigned int, st_order*, st_order*, Item*, st_order*, unsigned long long, select_result*, st_select_lex_unit*, st_select_lex*))[0x5619d6d4f817]
sql/sql_select.cc:454(handle_select(THD*, LEX*, select_result*, unsigned long))[0x5619d6d3ec32]
sql/sql_parse.cc:6463(execute_sqlcom_select(THD*, TABLE_LIST*))[0x5619d6d02e00]
sql/sql_parse.cc:3966(mysql_execute_command(THD*))[0x5619d6cf97c3]
sql/sql_prepare.cc:5024(Prepared_statement::execute(String*, bool))[0x5619d6d2863c]
sql/sql_prepare.cc:4493(Prepared_statement::execute_loop(String*, bool, unsigned char*, unsigned char*))[0x5619d6d269e3]
sql/sql_prepare.cc:3578(mysql_sql_stmt_execute(THD*))[0x5619d6d242c3]
sql/sql_parse.cc:3983(mysql_execute_command(THD*))[0x5619d6cf9808]
sql/sql_parse.cc:7998(mysql_parse(THD*, char*, unsigned int, Parser_state*, bool, bool))[0x5619d6d06d66]
sql/sql_parse.cc:1860(dispatch_command(enum_server_command, THD*, char*, unsigned int, bool, bool))[0x5619d6cf3383]
sql/sql_parse.cc:1379(do_command(THD*))[0x5619d6cf1ba7]
sql/sql_connect.cc:1420(do_handle_one_connection(CONNECT*))[0x5619d6e8bf11]
sql/sql_connect.cc:1325(handle_one_connection)[0x5619d6e8bc6d]
perfschema/pfs.cc:1871(pfs_spawn_thread)[0x5619d73ed20c]
/lib64/libpthread.so.0(+0x80a4)[0x7f08d7e810a4]
/lib64/libc.so.6(clone+0x6d)[0x7f08d72b904d]
 
Trying to get some variables.
Some pointers may be invalid and cause the dump to abort.
Query (0x7f08c00145b8): select * from v1 where r < 'x'



 Comments   
Comment by Igor Babaev [ 2023-04-21 ]

The following patch fixes the problem:

diff --git a/sql/item.h b/sql/item.h
index 31568aa..1e0caaa 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -7647,7 +7647,7 @@ class Item_direct_ref_to_item : public Item_direct_ref
   Item *get_tmp_table_item(THD *thd)
   { return m_item->get_tmp_table_item(thd); }
   Item *get_copy(THD *thd)
-  { return m_item->get_copy(thd); }
+  { return get_item_copy<Item_direct_ref_to_item>(thd, this); }
   COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
                           bool link_item_fields,
                           COND_EQUAL **cond_equal_ref)
@@ -7715,7 +7715,20 @@ class Item_direct_ref_to_item : public Item_direct_ref
   bool excl_dep_on_grouping_fields(st_select_lex *sel)
   { return m_item->excl_dep_on_grouping_fields(sel); }
   bool is_expensive() { return m_item->is_expensive(); }
-  Item* build_clone(THD *thd) { return get_copy(thd); }
+  void set_item(Item *item) { m_item= item; }
+  Item *build_clone(THD *thd)
+  {
+    Item *clone_item= m_item->build_clone(thd);
+    if (clone_item)
+    {
+      Item_direct_ref_to_item *copy= (Item_direct_ref_to_item *) get_copy(thd);
+      if (!copy)
+        return 0;
+      copy->set_item(clone_item);
+      return copy;
+    }
+    return 0;
+  }

Comment by Igor Babaev [ 2023-04-21 ]

This bug was introduced with the commit

commit 37a316c01d778a62a056d5d20110ef18bb55975e
Author:	Dmitry Shulga <dmitry.shulga@mariadb.com>  Fri Dec  9 06:10:25 2022
Committer:	Sergei Golubchik <serg@mariadb.org>  Sun Jan  1 15:04:03 2023

The patch added the class Item_direct_ref_to_item with such implementation of virtual methods get_copy() and build_clone()

+  Item *get_copy(THD *thd)
+  { return m_item->get_copy(thd); }
 
+  Item* build_clone(THD *thd) { return get_copy(thd); }

As a result whenever we use an object of the type Item_direct_ref_to_item r within an expression and for this expression build_clone() is called the sub-item starting with r is not actually cloned as it shares items it refers to with the cloned expression. When the clone is cleaned up the shared items of the original expression are also cleaned up. After this any traversal of the original expression may cause a crash. In the above test case the crash happened at an attempt to traverse the original cloned item when calling SELECT_LEX::update_used_tables().
In the above test case build_clone() is called when creating an item to be pushed into a view. Yet the method can be called in other situations.

Comment by Igor Babaev [ 2023-04-21 ]

Note that objects of the type Item_direct_ref_to_item in the current code can appear only at the first execution of prepared statement. They never appear if the query is executed directly.

Comment by Sergei Golubchik [ 2023-04-21 ]

From: IgorBabaev
To: commits@mariadb.org
Subject: [Commits] 60c1b15: MDEV-31102 Crash when pushing condition into view defined as union
Date: Fri, 21 Apr 2023 13:46:14 -0700 (PDT)

Comment by Sergei Golubchik [ 2023-04-24 ]

60c1b15 is ok to push

Comment by Igor Babaev [ 2023-04-24 ]

A fix for this bug was pushed into 10.4. The fix should be merged upstream as it is.

Generated at Thu Feb 08 10:21:17 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.