MDEV-18025 Mariabackup fails to detect corrupted page_compressed=1 tables

Problem:
=======
Mariabackup seems to fail to verify the pages of compressed tables.
The reason is that both fil_space_verify_crypt_checksum() and
buf_page_is_corrupted() will skip the validation for compressed pages.

Fix:
====
Mariabackup should call fil_page_decompress() for compressed and encrypted
compressed page. After that, call buf_page_is_corrupted() to
check the page corruption.
parent 84f119f2
......@@ -31,6 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "fil_cur.h"
#include "fil0crypt.h"
#include "fil0pagecompress.h"
#include "common.h"
#include "read_filt.h"
#include "xtrabackup.h"
......@@ -346,6 +347,7 @@ xb_fil_cur_read(
for (page = cursor->buf, i = 0; i < npages;
page += cursor->page_size, i++) {
ulint page_no = cursor->buf_page_no + i;
ulint page_type = mach_read_from_2(page + FIL_PAGE_TYPE);
if (cursor->space_id == TRX_SYS_SPACE
&& page_no >= FSP_EXTENT_SIZE
......@@ -367,12 +369,31 @@ xb_fil_cur_read(
memcpy(tmp_page, page, cursor->page_size);
if (!fil_space_decrypt(space, tmp_frame,
tmp_page, &decrypted)
|| buf_page_is_corrupted(true, tmp_page,
cursor->zip_size,
space)) {
tmp_page, &decrypted)) {
goto corrupted;
}
if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED) {
goto page_decomp;
}
if (buf_page_is_corrupted(
true, tmp_page, cursor->zip_size, space)) {
goto corrupted;
}
} else if (page_type == FIL_PAGE_PAGE_COMPRESSED) {
memcpy(tmp_page, page, cursor->page_size);
page_decomp:
ulint decomp = fil_page_decompress(tmp_frame, tmp_page);
if (!decomp
|| (decomp != srv_page_size && cursor->zip_size)
|| buf_page_is_corrupted(
true, tmp_page, cursor->zip_size, space)) {
goto corrupted;
}
} else if (buf_page_is_corrupted(true, page, cursor->zip_size,
space)) {
corrupted:
......
--innodb-encryption-rotate-key-age=2
--innodb-encryption-threads=4
--innodb-tablespaces-encryption
--plugin-load-add=$FILE_KEY_MANAGEMENT_SO
--loose-file-key-management
--loose-file-key-management-filename=$MYSQL_TEST_DIR/std_data/logkey.txt
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT, c char(200)) ENGINE=InnoDB page_compressed=yes encrypted=yes;
insert into t1(b, c) values("mariadb", "mariabackup");
# Corrupt the table
# xtrabackup backup
FOUND /Database page corruption detected/ in backup.log
drop table t1;
source include/have_file_key_management.inc;
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT, c char(200)) ENGINE=InnoDB page_compressed=yes encrypted=yes;
insert into t1(b, c) values("mariadb", "mariabackup");
let $MYSQLD_DATADIR=`select @@datadir`;
let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd;
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
--source include/shutdown_mysqld.inc
--echo # Corrupt the table
perl;
use strict;
use warnings;
use Fcntl qw(:DEFAULT :seek);
my $ibd_file = $ENV{'t1_IBD'};
my $chunk;
my $page_size = $ENV{'INNODB_PAGE_SIZE'};
sysopen IBD_FILE, $ibd_file, O_RDWR || die "Unable to open $ibd_file";
sysseek IBD_FILE, $page_size * 3 + 75, SEEK_CUR;
$chunk = '\xAA\xAA\xAA\xAA';
syswrite IBD_FILE, $chunk, 4;
close IBD_FILE;
EOF
--source include/start_mysqld.inc
echo # xtrabackup backup;
--disable_result_log
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
let $backuplog=$MYSQLTEST_VARDIR/tmp/backup.log;
--error 1
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir > $backuplog;
--enable_result_log
--let SEARCH_PATTERN=Database page corruption detected
--let SEARCH_FILE=$backuplog
--source include/search_pattern_in_file.inc
remove_file $backuplog;
drop table t1;
rmdir $targetdir;
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT, c char(200)) ENGINE=InnoDB page_compressed=yes;
insert into t1(b, c) values("mariadb", "mariabackup");
# Corrupt the table
# xtrabackup backup
FOUND /Database page corruption detected/ in backup.log
drop table t1;
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT, c char(200)) ENGINE=InnoDB page_compressed=yes;
insert into t1(b, c) values("mariadb", "mariabackup");
let $MYSQLD_DATADIR=`select @@datadir`;
let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd;
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
--source include/shutdown_mysqld.inc
--echo # Corrupt the table
perl;
use strict;
use warnings;
use Fcntl qw(:DEFAULT :seek);
my $ibd_file = $ENV{'t1_IBD'};
my $chunk;
my $page_size = $ENV{'INNODB_PAGE_SIZE'};
sysopen IBD_FILE, $ibd_file, O_RDWR || die "Unable to open $ibd_file";
sysseek IBD_FILE, 16384 * 3 + 75, SEEK_CUR;
$chunk = '\xAA\xAA\xAA\xAA';
syswrite IBD_FILE, $chunk, 4;
close IBD_FILE;
EOF
--source include/start_mysqld.inc
echo # xtrabackup backup;
--disable_result_log
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
let $backuplog=$MYSQLTEST_VARDIR/tmp/backup.log;
--error 1
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir > $backuplog;
--enable_result_log
--let SEARCH_PATTERN=Database page corruption detected
--let SEARCH_FILE=$backuplog
--source include/search_pattern_in_file.inc
remove_file $backuplog;
drop table t1;
rmdir $targetdir;
......@@ -452,6 +452,8 @@ static bool buf_page_decrypt_after_read(buf_page_t* bpage, fil_space_t* space)
decrypt. */
if (!fil_space_verify_crypt_checksum(
dst_frame, buf_page_get_zip_size(bpage))) {
decrypt_failed:
ib_logf(IB_LOG_LEVEL_ERROR,
"Encrypted page %u:%u in file %s"
" looks corrupted; key_version=" ULINTPF,
......@@ -460,7 +462,7 @@ static bool buf_page_decrypt_after_read(buf_page_t* bpage, fil_space_t* space)
mach_read_from_4(
FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
+ dst_frame));
decrypt_failed:
/* Mark page encrypted in case it should be. */
if (space->crypt_data->type
!= CRYPT_SCHEME_UNENCRYPTED) {
......@@ -4769,7 +4771,6 @@ static dberr_t buf_page_check_corrupt(buf_page_t* bpage, fil_space_t* space)
not anymore encrypted. */
corrupted = buf_page_is_corrupted(true, dst_frame, zip_size,
space);
if (!corrupted) {
bpage->encrypted = false;
} else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment