1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00
Files
mariadb/mysql-test/suite/rpl/r/rpl_row_trigger_multi_db.result
Brandon Nesterenko c2d44ecb90 MDEV-29894: Calling a function from a different database in a slave side trigger crashes
When opening and locking tables, if triggers will be invoked in a
separate database, thd->set_db() is invoked, thus freeeing the memory
and headers which thd->db had previously pointed to. In row based
replication, the event execution logic initializes thd->db to point
to the database which the event targets, which is owned by the
corresponding table share (introduced in d9898c9 for MDEV-7409).
The problem then, is that during the table opening and locking
process for a row event, memory which belongs to the table share
would be freed, which is not valid.

This patch replaces the thd->reset_db() calls to thd->set_db(),
which copies-by-value, rather than by reference. Then when the
memory is freed, our copy of memory is freed, rather than memory
which belongs to a table share.

Notes:
  1. The call to change thd->db now happens on a higher-level, in
Rows_log_event::do_apply_event() rather than ::do_exec_row(), in the
call stack. This is because do_exec_row() is called within a loop,
and each invocation would redundantly set and unset the db to the
same value.
  2. thd->set_db() is only used if triggers are to be invoked, as
there is no vulnerability in the non-trigger case, and copying
memory would be an unnecessary inefficiency.

Reviewed By:
============
Andrei Elkin <andrei.elkin@mariadb.com>
2023-06-21 12:51:01 -06:00

61 lines
1.3 KiB
Plaintext

include/master-slave.inc
[connection master]
connection slave;
set global slave_run_triggers_for_rbr=1;
connection master;
CREATE TABLE t1 (a int);
connection slave;
connection slave;
CREATE DATABASE db2;
CREATE FUNCTION db2.get_value(a INT) RETURNS int(2) RETURN 0;
#
# Test Insert_rows_log_event
connection slave;
CREATE TRIGGER tr_ins BEFORE INSERT ON t1 FOR EACH ROW BEGIN
DECLARE a INT;
SET a = db2.get_value(1);
END//
connection master;
INSERT INTO t1 VALUES (1);
connection slave;
connection slave;
DROP TRIGGER tr_ins;
#
# Test Update_rows_log_event
connection master;
INSERT INTO t1 VALUES (5);
connection slave;
connection slave;
CREATE TRIGGER tr_upd BEFORE UPDATE ON t1 FOR EACH ROW BEGIN
DECLARE a INT;
SET a = db2.get_value(1);
END//
connection master;
UPDATE t1 SET a=a+1 WHERE a=5;
connection slave;
connection slave;
DROP TRIGGER tr_upd;
#
# Test Delete_rows_log_event
connection master;
INSERT INTO t1 VALUES (7);
connection slave;
connection slave;
CREATE TRIGGER tr_del BEFORE DELETE ON t1 FOR EACH ROW BEGIN
DECLARE a INT;
SET a = db2.get_value(1);
END//
connection master;
DELETE FROM t1 WHERE a=7;
connection slave;
connection slave;
DROP TRIGGER tr_del;
#
# Cleanup
connection slave;
SET GLOBAL slave_run_triggers_for_rbr=NO;
DROP DATABASE db2;
connection master;
DROP TABLE t1;
include/rpl_end.inc