mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 20:12:31 +01:00
MDEV-23557 Galera heap-buffer-overflow in wsrep_rec_get_foreign_key
This commit contains a fix and extended test case for a ASAN failure reported during galera.fk mtr testing. The reported heap buffer overflow happens in test case where a cascading foreign key constraint is defined for a column of varchar type, and galera.fk.test has such vulnerable test scenario. Troubleshoting revealed that erlier fix for MDEV-19660 has made a fix for cascading delete handling to append wsrep keys from pcur->old_rec, in row_ins_foreign_check_on_constraint(). And, the ASAN failuer comes from later scanning of this old_rec reference. The fix in this commit, moves the call for wsrep_append_foreign_key() to happen somewhat earlier, and inside ongoing mtr, and using clust_rec which is set earlier in the same mtr for both update and delete cascade operations. for wsrep key populating, it does not matter when the keys are populated, all keys just have to be appended before wsrep transaction replicates. Note that I also tried similar fix for earlier wsrep key append, but using the old implementation with pcur->old_rec (instead of clust_rec), and same ASAN failure was reported. So it appears that pcur->old_rec is not properly set, to be used for wsrep key appending. galera.galera_fk_cascade_delete test has been extended by two new test scenarios: * FK cascade on varchar column. This test case reproduces same scenario as galera.fk, and this test scenario will also trigger ASAN failure with non fixed MariaDB versions. * multi-master conflict with FK cascading. this scenario causes a conflict between a replicated FK cascading transaction and local transaction trying to modify the cascaded child table row. Local transaction should be aborted and get deadlock error. This test scenario is passing both with old MariaDB version and with this commit as well.
This commit is contained in:
parent
5843dc485b
commit
df07ea0b27
5 changed files with 200 additions and 17 deletions
|
@ -1,3 +1,8 @@
|
|||
#
|
||||
# test phase with cascading foreign key through 3 tables
|
||||
#
|
||||
connection node_1;
|
||||
set wsrep_sync_wait=0;
|
||||
CREATE TABLE grandparent (
|
||||
id INT NOT NULL PRIMARY KEY
|
||||
) ENGINE=InnoDB;
|
||||
|
@ -19,14 +24,15 @@ INSERT INTO grandparent VALUES (1),(2);
|
|||
INSERT INTO parent VALUES (1,1), (2,2);
|
||||
INSERT INTO child VALUES (1,1), (2,2);
|
||||
connection node_2;
|
||||
set wsrep_sync_wait=0;
|
||||
DELETE FROM grandparent WHERE id = 1;
|
||||
connection node_1;
|
||||
SELECT COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
|
||||
COUNT(*) = 0
|
||||
1
|
||||
SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1;
|
||||
COUNT(*) = 0
|
||||
1
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
|
||||
COUNT(*) COUNT(*) = 0
|
||||
0 1
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM child WHERE parent_id = 1;
|
||||
COUNT(*) COUNT(*) = 0
|
||||
0 1
|
||||
DROP TABLE child;
|
||||
DROP TABLE parent;
|
||||
DROP TABLE grandparent;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
#
|
||||
# test phase with foreign key of varchar type
|
||||
#
|
||||
connection node_1;
|
||||
CREATE TABLE parent (
|
||||
`id` varchar(36) COLLATE utf8_unicode_ci NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
CREATE TABLE child (
|
||||
`id` int NOT NULL,
|
||||
`parent_id` varchar(36) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `parent_id` (`parent_id`),
|
||||
CONSTRAINT `ipallocations_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
INSERT INTO parent VALUES ('row one'), ('row two');
|
||||
INSERT INTO child VALUES (1,'row one'), (2,'row two');
|
||||
connection node_2;
|
||||
DELETE FROM parent;
|
||||
connection node_1;
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM parent;
|
||||
COUNT(*) COUNT(*) = 0
|
||||
0 1
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM child;
|
||||
COUNT(*) COUNT(*) = 0
|
||||
0 1
|
||||
DROP TABLE child;
|
||||
DROP TABLE parent;
|
||||
#
|
||||
# test phase with MM conflict in FK cascade
|
||||
#
|
||||
connection node_1;
|
||||
set wsrep_retry_autocommit=0;
|
||||
CREATE TABLE parent (
|
||||
id INT NOT NULL PRIMARY KEY
|
||||
) ENGINE=InnoDB;
|
||||
CREATE TABLE child (
|
||||
id INT NOT NULL PRIMARY KEY,
|
||||
j int default 0,
|
||||
parent_id INT,
|
||||
FOREIGN KEY (parent_id)
|
||||
REFERENCES parent(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
INSERT INTO parent VALUES (1);
|
||||
INSERT INTO child VALUES (1,0,1);
|
||||
connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1;
|
||||
SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
|
||||
connection node_2;
|
||||
DELETE FROM parent;
|
||||
connection node_1a;
|
||||
SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached";
|
||||
connection node_1;
|
||||
update child set j=2;;
|
||||
connection node_1a;
|
||||
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
|
||||
SET GLOBAL debug_dbug = "";
|
||||
SET DEBUG_SYNC = "RESET";
|
||||
connection node_1;
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM parent;
|
||||
COUNT(*) COUNT(*) = 0
|
||||
0 1
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM child;
|
||||
COUNT(*) COUNT(*) = 0
|
||||
0 1
|
||||
DROP TABLE child;
|
||||
DROP TABLE parent;
|
|
@ -3,7 +3,13 @@
|
|||
#
|
||||
|
||||
--source include/galera_cluster.inc
|
||||
--source include/have_innodb.inc
|
||||
|
||||
--echo #
|
||||
--echo # test phase with cascading foreign key through 3 tables
|
||||
--echo #
|
||||
|
||||
--connection node_1
|
||||
set wsrep_sync_wait=0;
|
||||
|
||||
CREATE TABLE grandparent (
|
||||
id INT NOT NULL PRIMARY KEY
|
||||
|
@ -30,11 +36,17 @@ INSERT INTO parent VALUES (1,1), (2,2);
|
|||
INSERT INTO child VALUES (1,1), (2,2);
|
||||
|
||||
--connection node_2
|
||||
set wsrep_sync_wait=0;
|
||||
|
||||
--let $wait_condition = SELECT COUNT(*) = 2 FROM child;
|
||||
--source include/wait_condition.inc
|
||||
DELETE FROM grandparent WHERE id = 1;
|
||||
|
||||
--connection node_1
|
||||
SELECT COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
|
||||
SELECT COUNT(*) = 0 FROM child WHERE parent_id = 1;
|
||||
--let $wait_condition = SELECT COUNT(*) = 1 FROM child;
|
||||
--source include/wait_condition.inc
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM parent WHERE grandparent_id = 1;
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM child WHERE parent_id = 1;
|
||||
|
||||
DROP TABLE child;
|
||||
DROP TABLE parent;
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
--source include/galera_cluster.inc
|
||||
--source include/have_debug.inc
|
||||
--source include/have_debug_sync.inc
|
||||
|
||||
--echo #
|
||||
--echo # test phase with foreign key of varchar type
|
||||
--echo #
|
||||
--connection node_1
|
||||
CREATE TABLE parent (
|
||||
`id` varchar(36) COLLATE utf8_unicode_ci NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
|
||||
CREATE TABLE child (
|
||||
`id` int NOT NULL,
|
||||
`parent_id` varchar(36) COLLATE utf8_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `parent_id` (`parent_id`),
|
||||
CONSTRAINT `ipallocations_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
||||
|
||||
INSERT INTO parent VALUES ('row one'), ('row two');
|
||||
INSERT INTO child VALUES (1,'row one'), (2,'row two');
|
||||
|
||||
--connection node_2
|
||||
--let $wait_condition = SELECT COUNT(*) = 2 FROM child;
|
||||
--source include/wait_condition.inc
|
||||
DELETE FROM parent;
|
||||
|
||||
--connection node_1
|
||||
--let $wait_condition = SELECT COUNT(*) = 0 FROM child;
|
||||
--source include/wait_condition.inc
|
||||
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM parent;
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM child;
|
||||
|
||||
DROP TABLE child;
|
||||
DROP TABLE parent;
|
||||
|
||||
--echo #
|
||||
--echo # test phase with MM conflict in FK cascade
|
||||
--echo #
|
||||
|
||||
--connection node_1
|
||||
set wsrep_retry_autocommit=0;
|
||||
CREATE TABLE parent (
|
||||
id INT NOT NULL PRIMARY KEY
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE child (
|
||||
id INT NOT NULL PRIMARY KEY,
|
||||
j int default 0,
|
||||
parent_id INT,
|
||||
FOREIGN KEY (parent_id)
|
||||
REFERENCES parent(id)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO parent VALUES (1);
|
||||
INSERT INTO child VALUES (1,0,1);
|
||||
|
||||
# block applier before applying
|
||||
--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1
|
||||
SET GLOBAL debug_dbug = "d,sync.wsrep_apply_cb";
|
||||
|
||||
--connection node_2
|
||||
--let $wait_condition = SELECT COUNT(*) = 1 FROM child;
|
||||
--source include/wait_condition.inc
|
||||
DELETE FROM parent;
|
||||
|
||||
--connection node_1a
|
||||
# wait until applier has reached the sync point
|
||||
SET SESSION DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_cb_reached";
|
||||
|
||||
|
||||
--connection node_1
|
||||
# issue conflicting write to child table, it should fail in certification
|
||||
--error ER_LOCK_DEADLOCK
|
||||
--send update child set j=2;
|
||||
|
||||
--connection node_1a
|
||||
# release the applier
|
||||
SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_cb";
|
||||
SET GLOBAL debug_dbug = "";
|
||||
SET DEBUG_SYNC = "RESET";
|
||||
|
||||
--connection node_1
|
||||
--reap
|
||||
|
||||
--let $wait_condition = SELECT COUNT(*) = 0 FROM child;
|
||||
--source include/wait_condition.inc
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM parent;
|
||||
SELECT COUNT(*), COUNT(*) = 0 FROM child;
|
||||
|
||||
DROP TABLE child;
|
||||
DROP TABLE parent;
|
|
@ -1402,20 +1402,20 @@ row_ins_foreign_check_on_constraint(
|
|||
btr_pcur_store_position(cascade->pcur, mtr);
|
||||
}
|
||||
|
||||
#ifdef WITH_WSREP
|
||||
err = wsrep_append_foreign_key(trx, foreign, clust_rec, clust_index,
|
||||
FALSE, WSREP_KEY_EXCLUSIVE);
|
||||
if (err != DB_SUCCESS) {
|
||||
ib::info() << "WSREP: foreign key append failed: " << err;
|
||||
goto nonstandard_exit_func;
|
||||
}
|
||||
#endif /* WITH_WSREP */
|
||||
mtr_commit(mtr);
|
||||
|
||||
ut_a(cascade->pcur->rel_pos == BTR_PCUR_ON);
|
||||
|
||||
cascade->state = UPD_NODE_UPDATE_CLUSTERED;
|
||||
|
||||
#ifdef WITH_WSREP
|
||||
err = wsrep_append_foreign_key(trx, foreign, cascade->pcur->old_rec, clust_index,
|
||||
FALSE, WSREP_KEY_EXCLUSIVE);
|
||||
if (err != DB_SUCCESS) {
|
||||
fprintf(stderr,
|
||||
"WSREP: foreign key append failed: %d\n", err);
|
||||
} else
|
||||
#endif /* WITH_WSREP */
|
||||
err = row_update_cascade_for_mysql(thr, cascade,
|
||||
foreign->foreign_table);
|
||||
|
||||
|
|
Loading…
Reference in a new issue