Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-42076

mariabackup takes datadir and innodb_log_group_home_dir from the server's SHOW VARIABLES answer instead of the same options given on the command line

    XMLWordPrintable

Details

    Description

      get_mysql_vars() in extra/mariabackup/backup_mysql.cc (the block commented "get some default values is they are missing from my.cnf") assigns datadir and innodb_log_group_home_dir from the server's SHOW VARIABLES answer unconditionally. An explicit -datadir or -innodb-log-group-home-dir on the command line is replaced, so mariabackup reads and copies the directory the server names, not the one it was told to use. No warning is printed (the existing "option 'datadir' has different values" message did not appear).

      The test runs mariadb-backup --backup against a fake server (perl) that answers like the real one. Control: the server reports the same directory as the option, and the backup completes. Case 1: the server reports another existing directory as datadir. Case 2: the same for innodb_log_group_home_dir. In both cases the real directory is on the command line.

      --source include/not_embedded.inc
      --source include/have_mariabackup.inc
       
      --echo #
      --echo # get_mysql_vars() (backup_mysql.cc) copies datadir and innodb_log_group_home_dir
      --echo # from the server's SHOW VARIABLES answer over the same options given on the
      --echo # mariabackup command line, without a warning.
      --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`
       
      --let $mode= 0
      while ($mode <= 2)
      {
        --exec echo $mode > $MYSQL_TMP_DIR/vuln_mode.txt
       
      perl;
      use IO::Socket::INET;
      open(my $mf, '<', "$ENV{MYSQL_TMP_DIR}/vuln_mode.txt") or die;
      my $M= <$mf> + 0;
      close $mf;
      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 $other= "$ENV{MYSQL_TMP_DIR}/";
      my %over= $M == 1 ? (datadir => $other) : $M == 2 ? (innodb_log_group_home_dir => $other) : ();
      my @vars= ((grep { !exists $over{$_->[0]} } @real), map { [$_, $over{$_}] } keys %over);
      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)], @vars) }
          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')`
        --let $opts= --datadir=$datadir
        if ($mode == 2)
        {
          --let $opts= --innodb-log-group-home-dir=$datadir
        }
        if ($mode == 0)
        {
          --echo # control: the server reports the same directory as the option
        }
        if ($mode == 1)
        {
          --echo # BUG if the server's datadir replaces --datadir
        }
        if ($mode == 2)
        {
          --echo # BUG if the server's innodb_log_group_home_dir replaces --innodb-log-group-home-dir
        }
       
        --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 $opts > $MYSQL_TMP_DIR/vuln_out.txt 2>&1
       
        --remove_file $MYSQL_TMP_DIR/vuln_port.txt
        --remove_file $MYSQL_TMP_DIR/vuln_mode.txt
        --remove_file $MYSQL_TMP_DIR/vuln_out.txt
        --exec rm -rf $MYSQL_TMP_DIR/vuln_backup
        --inc $mode
      }
       
      --remove_file $MYSQL_TMP_DIR/vuln_vars.txt
      --remove_file $MYSQL_TMP_DIR/vuln_status.txt

      The control passes; case 1 fails: the backup reads the redo log from the server's directory and stops with

      [00] Error: cannot read redo log header

      Case 2 (checked separately, with a control) fails the same way. Same on 12.3.4 (mariadb-backup). With the same command line and the real directory reported by the server, the backup completes.

      10.11.20-MariaDB-asan-debug-log source revision: 4ae8389be807b592fc909b99130350b4b8ced9b3
      12.3.4: f74586eebfe2a186087cfdda42be2d0ba398f01c

      Attachments

        Activity

          People

            serg Sergei Golubchik
            alice Alice Sherepa
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.