Details
-
Bug
-
Status: Open (View Workflow)
-
Minor
-
Resolution: Unresolved
-
10.11, 11.4, 11.8, 12.3
-
None
Description
innodb_init_param() in extra/mariabackup/xtrabackup.cc (line 2580 on 10.11, 2602 on 12.3) replaces srv_log_group_home_dir with the default only when it is NULL, so an empty --innodb-log-group-home-dir= is kept. recv_sys_t::find_checkpoint() then calls get_log_file_path() (storage/innobase/log/log0log.cc), which does path.back() on the empty string and trips the libstdc++ assertion.
No special server is needed; the test uses a fake server (perl) only because it needs no InnoDB. A non-empty value works (control). The behaviour on a release build was not checked.
--source include/not_embedded.inc
|
--source include/have_mariabackup.inc
|
|
|
--echo #
|
--echo # get_log_file_path() (storage/innobase/log/log0log.cc) calls path.back() on
|
--echo # srv_log_group_home_dir. mariabackup keeps an empty --innodb-log-group-home-dir=
|
--echo # (xtrabackup.cc replaces only a NULL value), so the string is empty.
|
--echo #
|
|
|
--exec $MYSQL -N -B -e "show variables" > $MYSQL_TMP_DIR/vuln_vars.txt
|
--exec $MYSQL -N -B -e "show status like 'innodb_lsn_flushed'" > $MYSQL_TMP_DIR/vuln_status.txt
|
--let $datadir= `SELECT @@datadir`
|
|
|
perl;
|
use IO::Socket::INET; |
sub pkt { my ($q, $b)= @_; substr(pack('V', length $b), 0, 3) . chr($q & 255) . $b } |
sub rd { my $s= shift; read($s, my $h, 4) or return; read($s, my $b, unpack('V', substr($h, 0, 3) . "\0")); (ord(substr $h, 3, 1) + 1, $b) } |
sub lstr { my $v= shift; my $n= length $v; ($n < 251 ? chr($n) : "\xfc" . pack('v', $n)) . $v } |
sub rs {
|
my ($s, $q, $cols, @rows)= @_; |
my $o= pkt($q++, chr(scalar @$cols));
|
$o.= pkt($q++, "\3def\0\0\0" . lstr($_) . lstr($_) . "\x0c" . pack('vVCvC', 33, 1024, 253, 0, 0) . "\0\0") for @$cols; |
$o.= pkt($q++, "\xfe" . pack('vv', 0, 2)); |
$o.= pkt($q++, join '', map { defined $_ ? lstr($_) : "\xfb" } @$_) for @rows; |
print $s $o, pkt($q, "\xfe" . pack('vv', 0, 2)); |
}
|
sub slurp { open(my $h, '<', $_[0]) or die; my @r= map { chomp; [split /\t/, $_, 2] } <$h>; close $h; @r } |
my @real= slurp("$ENV{MYSQL_TMP_DIR}/vuln_vars.txt"); |
my @stat= slurp("$ENV{MYSQL_TMP_DIR}/vuln_status.txt"); |
my $l= IO::Socket::INET->new(Listen => 5, LocalAddr => '127.0.0.1', LocalPort => 0) or die; |
open(my $f, '>', "$ENV{MYSQL_TMP_DIR}/vuln_port.txt") or die; |
print $f $l->sockport;
|
close $f; |
fork and exit; |
open(STDIN, '<', '/dev/null'); open(STDOUT, '>', '/dev/null'); open(STDERR, '>&', \*STDOUT); |
alarm 120;
|
while (my $s= $l->accept) {
|
next if fork; |
alarm 120;
|
close $l; |
my $ok= sub { print $s pkt($_[0], "\0\0\0" . pack('vv', 2, 0)) }; |
print $s pkt(0, "\x0a10.11.6-MariaDB\0" . pack('V', 7) . "AAAAAAAA\0" . pack('v', 0xa20d) . "\x2d" . pack('v', 2) . pack('v', 8) . chr(21) . "\0" x 10 . "A" x 12 . "\0mysql_native_password\0"); |
my ($q)= rd($s);
|
print $s pkt($q, "\0\0\0" . pack('vv', 2, 0)); |
while (my ($q, $b)= rd($s)) {
|
my $sql= lc substr($b, 1);
|
if ($sql =~ /^show variables like/) { rs($s, $q, [qw(k v)]) } |
elsif ($sql =~ /^show variables/) { rs($s, $q, [qw(k v)], @real) } |
elsif ($sql =~ /^show (global )?status like 'innodb_lsn_flushed'/) { rs($s, $q, [qw(k v)], @stat) } |
elsif ($sql =~ /^show grants/) { rs($s, $q, ['Grants'], ["GRANT ALL PRIVILEGES ON *.* TO 'root'\@'%' WITH GRANT OPTION"]) } |
elsif ($sql =~ /innodb_encrypt_log/) { rs($s, $q, ['x'], ['0']) } |
elsif ($sql =~ /innodb_encrypt_tables/) { rs($s, $q, ['x'], ['OFF']) } |
elsif ($sql =~ /^(select|show)/) { rs($s, $q, ['x']) } |
else { $ok->($q) } } |
exit }
|
EOF
|
|
|
--let $fake_port= `SELECT LOAD_FILE('$MYSQL_TMP_DIR/vuln_port.txt')`
|
|
|
--echo # control: a non-empty --innodb-log-group-home-dir works
|
--exec timeout 100 $XTRABACKUP --no-defaults --backup --target-dir=$MYSQL_TMP_DIR/vuln_backup --host=127.0.0.1 --port=$fake_port --user=root --password=x --skip-ssl --datadir=$datadir --innodb-log-group-home-dir=$datadir > $MYSQL_TMP_DIR/vuln_out.txt 2>&1
|
--exec rm -rf $MYSQL_TMP_DIR/vuln_backup
|
|
|
--echo # BUG if an empty --innodb-log-group-home-dir= aborts mariabackup
|
--error 0,1
|
--exec timeout 100 $XTRABACKUP --no-defaults --backup --target-dir=$MYSQL_TMP_DIR/vuln_backup --host=127.0.0.1 --port=$fake_port --user=root --password=x --skip-ssl --datadir=$datadir --innodb-log-group-home-dir= > $MYSQL_TMP_DIR/vuln_out.txt 2>&1
|
|
|
--remove_file $MYSQL_TMP_DIR/vuln_port.txt
|
--remove_file $MYSQL_TMP_DIR/vuln_out.txt
|
--remove_file $MYSQL_TMP_DIR/vuln_vars.txt
|
--remove_file $MYSQL_TMP_DIR/vuln_status.txt
|
--exec rm -rf $MYSQL_TMP_DIR/vuln_backup
|
The control passes; the second command fails with command ... failed with wrong error: 134:
10.11.20-MariaDB-asan-debug-log source revision: 4ae8389be807b592fc909b99130350b4b8ced9b3
|
/usr/include/c++/13/bits/basic_string.h:1341: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::reference std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::back() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; reference = char&]: Assertion '!empty()' failed.
|
Aborted
|
|
|
#6 std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::back /usr/include/c++/13/bits/basic_string.h:1341
|
#7 get_log_file_path[abi:cxx11](char const*) /10.11/src/storage/innobase/log/log0log.cc:1552
|
#8 recv_sys_t::find_checkpoint /10.11/src/storage/innobase/log/log0recv.cc:1749
|
#9 xtrabackup_backup_func /10.11/src/extra/mariabackup/xtrabackup.cc:5664
|
#10 main_low /10.11/src/extra/mariabackup/xtrabackup.cc:8020
|
#11 main /10.11/src/extra/mariabackup/xtrabackup.cc:7795
|
Same on 12.3.4 (f74586eebfe2a186087cfdda42be2d0ba398f01c).