summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Malyavin <nikitamalyavin@gmail.com>2019-07-30 22:39:55 +1000
committerNikita Malyavin <nikitamalyavin@gmail.com>2019-10-11 17:02:39 +1000
commit350e46a8b52b20c7ddd311ca56f12ab77ffae0ae (patch)
tree99b86d24ba8a3dc8a924b3da0b825a3ac7100820
parentb393e2cb0c079b30563dcc87a62002c9c778643c (diff)
downloadmariadb-git-350e46a8b52b20c7ddd311ca56f12ab77ffae0ae.tar.gz
MDEV-18546 ASAN heap-use-after-free in innobase_get_computed_value / row_purge
the bug was already fixed in MDEV-17005, so now only test is added
-rw-r--r--mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result45
-rw-r--r--mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test64
-rw-r--r--storage/innobase/row/row0vers.cc1
3 files changed, 110 insertions, 0 deletions
diff --git a/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result b/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result
index 647ba47a880..0309f084e5d 100644
--- a/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result
+++ b/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result
@@ -233,3 +233,48 @@ set global debug_dbug= @saved_dbug;
drop table t1;
set debug_sync=reset;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;
+#
+# MDEV-18546 ASAN heap-use-after-free
+# in innobase_get_computed_value / row_purge
+#
+CREATE TABLE t1 (
+pk INT AUTO_INCREMENT,
+b BIT(15),
+v BIT(15) AS (b) VIRTUAL,
+PRIMARY KEY(pk),
+UNIQUE(v)
+) ENGINE=InnoDB;
+INSERT IGNORE INTO t1 (b) VALUES
+(NULL),(b'011'),(b'000110100'),
+(b'01101101010'),(b'01111001001011'),(NULL);
+SET GLOBAL innodb_debug_sync = "ib_clust_v_col_before_row_allocated "
+ "SIGNAL before_row_allocated "
+ "WAIT_FOR flush_unlock";
+SET GLOBAL innodb_debug_sync = "ib_open_after_dict_open "
+ "SIGNAL purge_open "
+ "WAIT_FOR select_open";
+set @saved_dbug= @@global.debug_dbug;
+set global debug_dbug= "+d,ib_purge_virtual_index_callback";
+connect purge_waiter,localhost,root;
+SET debug_sync= "now WAIT_FOR before_row_allocated";
+connection default;
+REPLACE INTO t1 (pk, b) SELECT pk, b FROM t1;
+connection purge_waiter;
+connection default;
+disconnect purge_waiter;
+FLUSH TABLES;
+SET GLOBAL innodb_debug_sync = reset;
+SET debug_sync= "now SIGNAL flush_unlock WAIT_FOR purge_open";
+SET GLOBAL innodb_debug_sync = reset;
+SET debug_sync= "ib_open_after_dict_open SIGNAL select_open";
+SELECT * FROM t1;
+pk b v
+1 NULL NULL
+2
+3
+4 j j
+5 K K
+6 NULL NULL
+DROP TABLE t1;
+SET debug_sync= reset;
+set global debug_dbug= @saved_dbug;
diff --git a/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test b/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test
index ee88789dec4..4ea3c9fd34b 100644
--- a/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test
+++ b/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test
@@ -323,3 +323,67 @@ drop table t1;
--source include/wait_until_count_sessions.inc
set debug_sync=reset;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;
+
+--echo #
+--echo # MDEV-18546 ASAN heap-use-after-free
+--echo # in innobase_get_computed_value / row_purge
+--echo #
+
+CREATE TABLE t1 (
+ pk INT AUTO_INCREMENT,
+ b BIT(15),
+ v BIT(15) AS (b) VIRTUAL,
+ PRIMARY KEY(pk),
+ UNIQUE(v)
+) ENGINE=InnoDB;
+INSERT IGNORE INTO t1 (b) VALUES
+ (NULL),(b'011'),(b'000110100'),
+ (b'01101101010'),(b'01111001001011'),(NULL);
+
+SET GLOBAL innodb_debug_sync = "ib_clust_v_col_before_row_allocated "
+ "SIGNAL before_row_allocated "
+ "WAIT_FOR flush_unlock";
+SET GLOBAL innodb_debug_sync = "ib_open_after_dict_open "
+ "SIGNAL purge_open "
+ "WAIT_FOR select_open";
+
+# In 10.2 trx_undo_roll_ptr_is_insert(t_roll_ptr) condition never pass in purge,
+# so this condition is forced to pass in row_vers_old_has_index_entry
+set @saved_dbug= @@global.debug_dbug;
+set global debug_dbug= "+d,ib_purge_virtual_index_callback";
+
+# The purge starts from REPLACE command. To avoid possible race, separate
+# connection is used.
+--connect(purge_waiter,localhost,root)
+--send
+SET debug_sync= "now WAIT_FOR before_row_allocated";
+
+--connection default
+REPLACE INTO t1 (pk, b) SELECT pk, b FROM t1;
+
+--connection purge_waiter
+# Now we will definitely catch ib_clust_v_col_before_row_allocated
+--reap
+--connection default
+--disconnect purge_waiter
+
+# purge hangs on the sync point. table is purged, ref_count is set to 0
+FLUSH TABLES;
+
+# Avoid hang on repeating purge.
+# Reset Will be applied after first record is purged
+SET GLOBAL innodb_debug_sync = reset;
+
+SET debug_sync= "now SIGNAL flush_unlock WAIT_FOR purge_open";
+
+# Avoid hang on repeating purge
+SET GLOBAL innodb_debug_sync = reset;
+
+# select unblocks purge thread
+SET debug_sync= "ib_open_after_dict_open SIGNAL select_open";
+SELECT * FROM t1;
+
+# Cleanup
+DROP TABLE t1;
+SET debug_sync= reset;
+set global debug_dbug= @saved_dbug; \ No newline at end of file
diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc
index 03e586d4bee..f2a574b8331 100644
--- a/storage/innobase/row/row0vers.cc
+++ b/storage/innobase/row/row0vers.cc
@@ -467,6 +467,7 @@ row_vers_build_clust_v_col(
vcol_info->set_used();
maria_table = vcol_info->table();
}
+ DEBUG_SYNC(current_thd, "ib_clust_v_col_before_row_allocated");
innobase_allocate_row_for_vcol(thd, index,
&local_heap,