Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions mysql-test/suite/galera/r/galera_sst_buffer_pool_injection.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
connection node_2;
connection node_1;
SELECT 1;
1
1
connection node_1;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
connection node_2;
buffer-pool injection prevented
FOUND 1 /Running: .*mariadb-backup --prepare/ in mysqld.2.err
connection node_2;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST .* failed');
28 changes: 28 additions & 0 deletions mysql-test/suite/galera/r/galera_sst_datadir_injection.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
connection node_2;
connection node_1;
SELECT 1;
1
1
FOUND 1 /wsrep_sst_rsync/ in mysqld.1.err
connection node_1;
call mtr.add_suppression('unsafe for shell interpolation');
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
connection node_2;
connection node_2;
FOUND 1 /unsafe for shell interpolation/ in mysqld.2.err
datadir injection prevented
connection node_2;
call mtr.add_suppression('Illegal character in variable');
call mtr.add_suppression('unsafe for shell interpolation');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('Failed to read .* from: wsrep_sst_rsync');
call mtr.add_suppression('Failed to prepare for .* SST');
call mtr.add_suppression('SST preparation failed');
call mtr.add_suppression('SST request callback failed');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('Parent mysqld process .* terminated unexpectedly');
call mtr.add_suppression('Cleanup after exit with status');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST .* failed');
21 changes: 21 additions & 0 deletions mysql-test/suite/galera/r/galera_sst_datadir_spaces.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
connection node_2;
connection node_1;
SELECT 1;
1
1
connection node_1;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
connection node_2;
connection node_2;
SELECT 1;
1
1
connection node_2;
connection node_2;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST .* failed');
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
connection node_2;
connection node_1;
connection node_1;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST failed');
call mtr.add_suppression('SST preparation failed');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('unsafe for shell interpolation');
call mtr.add_suppression('Illegal character in variable');
connection node_2;
FOUND 1 /unsafe for shell interpolation/ in mysqld.1.err
donor-side datadir injection prevented
FOUND 1 /Parent mysqld process .* terminated unexpectedly/ in mysqld.2.err
# restart
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
connection node_2;
connection node_1;
connection node_1;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST failed');
call mtr.add_suppression('SST preparation failed');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('unsafe for shell interpolation');
call mtr.add_suppression('Illegal character in variable');
CREATE USER 'sst';
GRANT ALL PRIVILEGES ON *.* TO 'sst';
SET GLOBAL wsrep_sst_auth = 'sst:';
connection node_2;
FOUND 1 /unsafe for shell interpolation/ in mysqld.1.err
donor-side datadir injection prevented
connection node_1;
SET GLOBAL wsrep_sst_auth = '';
DROP USER 'sst';
connection node_2;
# restart
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
18 changes: 18 additions & 0 deletions mysql-test/suite/galera/r/galera_sst_mysqld_args_injection.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
connection node_2;
connection node_1;
SELECT 1;
1
1
connection node_1;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');
connection node_2;
backquote injection prevented
connection node_2;
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST .* failed');
call mtr.add_suppression('InnoDB: Cannot open .* for writing');
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
connection node_2;
connection node_1;
REJECTED_newline
PASS_clean
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
!include ../galera_2nodes.cnf

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why these test are run only for either rsync or mariabackup not both and especially not for mysqldump?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why rsync-only for some, mariabackup-only for others?

  • galera_sst_datadir_spaces and galera_sst_rsyncd_conf_injection test FILTER (the rsync donor's transfer exclude-list) and the rsyncd.conf heredoc - code that only exists in wsrep_sst_rsync.sh. mariabackup and mysqldump have no equivalent construct, so there's nothing for those methods to exercise here.
  • galera_sst_buffer_pool_injection tests INNOEXTRA/setup_commands, which is mariadb-backup-specific - innodb-buffer-pool-filename is only ever consumed by mariadb-backup; rsync and mysqldump never read it.
  • The datadir_injection/mysqld_args_injection these two test C++ code (sst_prepare_other's datadir check, copy_orig_argv's escaping in sql/wsrep_sst.cc) that's shared verbatim between rsync and mariabackup - method is just a string substituted into the command template, the validation logic itself doesn't branch on it. The rsync was picked as one of representative method rather than duplicating the same C++-level test per script; re-running with mariabackup would exercise the identical check.

And for why mysqldump specifically is missing?

  • sst_prepare_mysqldump() (the joiner side, used only for wsrep_sst_method=mysqldump) never builds a shell command at all: no sh -c, no --datadir, no copy_orig_argv. It just formats an addr:port string and starts a monitor thread. So all four of my reproduction tests, which put the malicious value on the joiner and force it to request SST, have nothing to exercise on mysqldump's joiner side - there's no sink there.


[mysqld]
wsrep_sst_method=mariabackup
wsrep_sst_auth="root:"
wsrep_debug=1
76 changes: 76 additions & 0 deletions mysql-test/suite/galera/t/galera_sst_buffer_pool_injection.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#
# mariabackup SST must pass innodb-buffer-pool-filename to mariadb-backup as a
# literal argument, so that shell metacharacters in it are not executed.
#
# Steps:
# 1. Bring up a 2-node mariabackup-SST cluster.
# 2. Force a full SST on node_2, restarting it with an
# innodb-buffer-pool-filename carrying a "touch <marker>" payload. It has
# to be on the command line: the SST script has no config-file fallback
# for this option.
# 3. Assert node_2 joins, the payload did not run, and the mariadb-backup
# command line is still logged.
# 4. Restart node_2 cleanly and rejoin.
#

--source include/galera_cluster.inc
--source include/have_innodb.inc
--source include/have_mariabackup.inc

SELECT 1;

--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc

--connection node_1
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');

--connection node_2
--source include/shutdown_mysqld.inc

--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
perl;
unlink "$ENV{MYSQL_TMP_DIR}/bp_inj_marker";
EOF

# $start_mysqld_params is split on whitespace with no quote handling, so the
# payload must be a single space-free token; ">file" (a bare redirection,
# needs no command name or space) is used as the marker-creation payload.
--let $start_mysqld_params = --innodb-buffer-pool-filename=x'>$MYSQL_TMP_DIR/bp_inj_marker&'y
--source include/start_mysqld.inc
--let $start_mysqld_params =

--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc

perl;
die "FAIL: marker created - buffer-pool injection was NOT prevented\n"
if -e "$ENV{MYSQL_TMP_DIR}/bp_inj_marker";
print "buffer-pool injection prevented\n";
EOF

# SST failures are diagnosed from the logged command line
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.2.err
--let SEARCH_PATTERN = Running: .*mariadb-backup --prepare
--source include/search_pattern_in_file.inc

# restart node_2 cleanly (no malicious arg) and rejoin
--connection node_2
--source include/shutdown_mysqld.inc
--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat
--source include/start_mysqld.inc

--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc

perl;
unlink "$ENV{MYSQL_TMP_DIR}/bp_inj_marker";
EOF

call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST .* failed');
5 changes: 5 additions & 0 deletions mysql-test/suite/galera/t/galera_sst_datadir_injection.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
!include ../galera_2nodes.cnf

[mysqld]
wsrep_sst_method=rsync
wsrep_debug=1
97 changes: 97 additions & 0 deletions mysql-test/suite/galera/t/galera_sst_datadir_injection.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#
# mysqld must reject a datadir containing shell metacharacters before it
# builds the SST command line that it runs via sh -c.
#
# Steps:
# 1. Bring up a 2-node rsync-SST cluster.
# 2. Move node 2's datadir to a name carrying a "touch <marker>" payload and
# force a full SST.
# 3. Assert node 2 refuses the datadir and the payload did not run.
# 4. Restore the datadir and rejoin.
#

--source include/galera_cluster.inc
--source include/have_innodb.inc

SELECT 1;

--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc

# sanity: initial SST used rsync
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err
--let SEARCH_PATTERN = wsrep_sst_rsync
--source include/search_pattern_in_file.inc

--connection node_1
call mtr.add_suppression('unsafe for shell interpolation');
call mtr.add_suppression('Process completed with error');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('Will never receive state. Need to abort');

--connection node_2
--source include/shutdown_mysqld.inc

# move the datadir to a name carrying a shell-injection payload
perl;
use strict;
use File::Path;
my $tmp = $ENV{MYSQL_TMP_DIR};
my $vardir = $ENV{MYSQLTEST_VARDIR};
my $marker = "$tmp/datadir_inj_marker";
my $ddir = "$tmp/dd_inj'&touch $marker&'x";
my $cnf = "$vardir/my.cnf";
unlink $marker;
(my $parent = $ddir) =~ s{/[^/]+$}{};
mkpath($parent);
rename("$vardir/mysqld.2/data", $ddir) or die "rename datadir: $!";
unlink "$ddir/grastate.dat";
open(my $sz, '>', "$tmp/inj_cnf_size") or die $!; print $sz -s $cnf; close $sz;
open(my $fh, '>>', $cnf) or die $!; print $fh "[mysqld.2]\ndatadir=\"$ddir\"\n"; close $fh;
EOF

--connection node_2
--error 1,134
--exec $MYSQLD_LAST_CMD

--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.2.err
--let SEARCH_PATTERN = unsafe for shell interpolation
--source include/search_pattern_in_file.inc

perl;
die "FAIL: marker created - datadir injection was NOT prevented\n"
if -e "$ENV{MYSQL_TMP_DIR}/datadir_inj_marker";
print "datadir injection prevented\n";
EOF

# restore datadir and rejoin
perl;
use strict;
my $tmp = $ENV{MYSQL_TMP_DIR};
my $vardir = $ENV{MYSQLTEST_VARDIR};
my $cnf = "$vardir/my.cnf";
open(my $sz, '<', "$tmp/inj_cnf_size") or die $!; my $orig = <$sz>; close $sz;
truncate($cnf, $orig) or die "truncate: $!";
unlink "$tmp/datadir_inj_marker", "$tmp/inj_cnf_size";
rename("$tmp/dd_inj'&touch $tmp/datadir_inj_marker&'x", "$vardir/mysqld.2/data")
or die "restore datadir: $!";
EOF

--connection node_2
--source include/start_mysqld.inc
--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'
--source include/wait_condition.inc

# suppress the rejected-SST noise on the final node_2 instance
call mtr.add_suppression('Illegal character in variable');
call mtr.add_suppression('unsafe for shell interpolation');
call mtr.add_suppression('WSREP_SST:');
call mtr.add_suppression('Failed to read .* from: wsrep_sst_rsync');
call mtr.add_suppression('Failed to prepare for .* SST');
call mtr.add_suppression('SST preparation failed');
call mtr.add_suppression('SST request callback failed');
call mtr.add_suppression('Will never receive state. Need to abort');
call mtr.add_suppression('Parent mysqld process .* terminated unexpectedly');
call mtr.add_suppression('Cleanup after exit with status');
call mtr.add_suppression('State transfer to .* failed');
call mtr.add_suppression('SST .* failed');
5 changes: 5 additions & 0 deletions mysql-test/suite/galera/t/galera_sst_datadir_spaces.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
!include ../galera_2nodes.cnf

[mysqld]
wsrep_sst_method=rsync
wsrep_debug=1
Loading