[MDEV-26228] Heap-use-after-free on indexed virtual column with ON UPDATE CASCADE Created: 2021-07-23  Updated: 2021-07-23  Resolved: 2021-07-23

Status: Closed
Project: MariaDB Server
Component/s: Storage Engine - InnoDB, Virtual Columns
Affects Version/s: 10.2.2, 10.3.0, 10.4.0, 10.5.0, 10.6.0
Fix Version/s: 10.2.40, 10.3.31, 10.4.21, 10.5.12, 10.6.4

Type: Bug Priority: Major
Reporter: Marko Mäkelä Assignee: Marko Mäkelä
Resolution: Fixed Votes: 0
Labels: ASAN, corruption

Issue Links:
Relates
relates to MDEV-24041 Generated column DELETE with FOREIGN ... Closed
relates to MDEV-25466 Merge new release of InnoDB 5.7.34 to... Closed

 Description   

The following change to the MDEV-24041 test case triggers heap-use-after-free:

diff --git a/mysql-test/suite/gcol/t/innodb_virtual_fk.test b/mysql-test/suite/gcol/t/innodb_virtual_fk.test
index 24b6a4631e6..c99259531b3 100644
--- a/mysql-test/suite/gcol/t/innodb_virtual_fk.test
+++ b/mysql-test/suite/gcol/t/innodb_virtual_fk.test
@@ -670,6 +670,7 @@ CREATE TABLE email_stats (
   PRIMARY KEY (id),
   KEY mautic_generated_sent_date_email_id (generated_email_id),
   FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE SET NULL
+  ON UPDATE CASCADE
 ) ENGINE=InnoDB;
 
 
@@ -677,6 +678,7 @@ CREATE TABLE emails_metadata (
   email_id int,
   PRIMARY KEY (email_id),
   CONSTRAINT FK FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE CASCADE
+  ON UPDATE CASCADE
 ) ENGINE=InnoDB;
 
 
@@ -684,6 +686,7 @@ INSERT INTO emails VALUES (1);
 INSERT INTO email_stats (id, email_id,  date_sent) VALUES (1,1,'Jan');
 INSERT INTO emails_metadata VALUES (1);
 
+UPDATE emails SET id=2;
 DELETE FROM emails;
 
 DROP TABLE email_stats;

The fix is simple:

diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc
--- a/storage/innobase/row/row0ins.cc
+++ b/storage/innobase/row/row0ins.cc
@@ -969,8 +969,8 @@ row_ins_foreign_fill_virtual(
 		upd_field = update->fields + n_diff;
 
 		upd_field->old_v_val = static_cast<dfield_t*>(
-				mem_heap_alloc(cascade->heap,
-					sizeof *upd_field->old_v_val));
+			mem_heap_alloc(update->heap,
+				       sizeof *upd_field->old_v_val));
 
 		dfield_copy(upd_field->old_v_val, vfield);
 

The failure without the fix would look like this:

10.2 742b3a0d39875584f994f4bd01d57cc31d1dddca

==1699592==ERROR: AddressSanitizer: heap-use-after-free on address 0x616000389070 at pc 0x0000017603bb bp 0x7fac625c7290 sp 0x7fac625c7288
READ of size 8 at 0x616000389070 thread T27
    #0 0x17603ba in dfield_copy_data(dfield_t*, dfield_t const*) /mariadb/10.2o/storage/innobase/include/data0data.ic:143:25
    #1 0x17603ba in row_upd_store_v_row(upd_node_t*, upd_t const*, THD*, TABLE*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:2139:5
    #2 0x17603ba in row_upd_store_row(upd_node_t*, THD*, TABLE*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:2238:13
    #3 0x17549a5 in row_upd_clust_step(upd_node_t*, que_thr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:3187:7
    #4 0x17549a5 in row_upd(upd_node_t*, que_thr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:3281:9
    #5 0x1752b06 in row_upd_step(que_thr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:3427:8
    #6 0x167b1c5 in row_update_cascade_for_mysql(que_thr_t*, upd_node_t*, dict_table_t*) /mariadb/10.2o/storage/innobase/row/row0mysql.cc:2106:4
    #7 0x161ec00 in row_ins_foreign_check_on_constraint(que_thr_t*, dict_foreign_t*, btr_pcur_t*, dtuple_t*, mtr_t*) /mariadb/10.2o/storage/innobase/row/row0ins.cc:1360:8
    #8 0x161ec00 in row_ins_check_foreign_constraint(unsigned long, dict_foreign_t*, dict_table_t*, dtuple_t*, que_thr_t*) /mariadb/10.2o/storage/innobase/row/row0ins.cc:1749:12
    #9 0x1760f20 in row_upd_check_references_constraints(upd_node_t*, btr_pcur_t*, dict_table_t*, dict_index_t*, unsigned short*, que_thr_t*, mtr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:323:10
    #10 0x1758dc3 in row_upd_clust_rec_by_insert(upd_node_t*, dict_index_t*, que_thr_t*, bool, bool, mtr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:2787:10
    #11 0x1758dc3 in row_upd_clust_step(upd_node_t*, que_thr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:3207:9
    #12 0x1758dc3 in row_upd(upd_node_t*, que_thr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:3281:9
    #13 0x1752b06 in row_upd_step(que_thr_t*) /mariadb/10.2o/storage/innobase/row/row0upd.cc:3427:8
    #14 0x167740f in row_update_for_mysql(row_prebuilt_t*) /mariadb/10.2o/storage/innobase/row/row0mysql.cc:1822:3
    #15 0x13b7101 in ha_innobase::update_row(unsigned char const*, unsigned char*) /mariadb/10.2o/storage/innobase/handler/ha_innodb.cc:9019:10
    #16 0xe0ad05 in handler::ha_update_row(unsigned char const*, unsigned char*) /mariadb/10.2o/sql/handler.cc:6150:3
    #17 0xab8865 in mysql_update(THD*, TABLE_LIST*, List<Item>&, List<Item>&, Item*, unsigned int, st_order*, unsigned long long, enum_duplicates, bool, unsigned long long*, unsigned long long*) /mariadb/10.2o/sql/sql_update.cc:823:24
    #18 0x87286a in mysql_execute_command(THD*) /mariadb/10.2o/sql/sql_parse.cc:4056:21
    #19 0x866cfc in mysql_parse(THD*, char*, unsigned int, Parser_state*, bool, bool) /mariadb/10.2o/sql/sql_parse.cc:7793:18
    #20 0x85d321 in dispatch_command(enum_server_command, THD*, char*, unsigned int, bool, bool) /mariadb/10.2o/sql/sql_parse.cc:1827:7
    #21 0x863cc7 in do_command(THD*) /mariadb/10.2o/sql/sql_parse.cc:1381:17
    #22 0xb77d90 in do_handle_one_connection(CONNECT*) /mariadb/10.2o/sql/sql_connect.cc:1336:11
    #23 0xb777f5 in handle_one_connection /mariadb/10.2o/sql/sql_connect.cc:1241:3
    #24 0x7fac75703ea6 in start_thread nptl/pthread_create.c:477:8
    #25 0x7fac74d49dee in clone misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:95
 
0x616000389070 is located 496 bytes inside of 640-byte region [0x616000388e80,0x616000389100)
freed by thread T27 here:
    #0 0x62b2b2 in free (/dev/shm/10.2a/sql/mysqld+0x62b2b2)
    #1 0x175d0ef in mem_heap_free_heap_top(mem_block_info_t*, unsigned char*) /mariadb/10.2o/storage/innobase/include/mem0mem.ic:259:3
    #2 0x175d0ef in mem_heap_empty(mem_block_info_t*) /mariadb/10.2o/storage/innobase/include/mem0mem.ic:289:2
 
previously allocated by thread T27 here:
    #0 0x62b51d in malloc (/dev/shm/10.2a/sql/mysqld+0x62b51d)
    #1 0x152f7fd in mem_heap_create_block_func(mem_block_info_t*, unsigned long, char const*, unsigned int, unsigned long) /mariadb/10.2o/storage/innobase/mem/mem0mem.cc:289:37
    #2 0x152fe9b in mem_heap_add_block(mem_block_info_t*, unsigned long) /mariadb/10.2o/storage/innobase/mem/mem0mem.cc:390:14


Generated at Thu Feb 08 09:43:43 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.