From 171271edf8204d6a958a00631ea6ce4050f84b9e Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 18 Dec 2018 18:07:17 +0530 Subject: [PATCH 1/2] 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. --- extra/mariabackup/fil_cur.cc | 29 ++++++++++-- .../mariabackup/encrypted_page_compressed.opt | 6 +++ .../encrypted_page_compressed.result | 6 +++ .../encrypted_page_compressed.test | 47 +++++++++++++++++++ .../unencrypted_page_compressed.result | 6 +++ .../unencrypted_page_compressed.test | 46 ++++++++++++++++++ storage/innobase/buf/buf0buf.cc | 5 +- 7 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 mysql-test/suite/mariabackup/encrypted_page_compressed.opt create mode 100644 mysql-test/suite/mariabackup/encrypted_page_compressed.result create mode 100644 mysql-test/suite/mariabackup/encrypted_page_compressed.test create mode 100644 mysql-test/suite/mariabackup/unencrypted_page_compressed.result create mode 100644 mysql-test/suite/mariabackup/unencrypted_page_compressed.test diff --git a/extra/mariabackup/fil_cur.cc b/extra/mariabackup/fil_cur.cc index c2693e6f616..b677e9973b7 100644 --- a/extra/mariabackup/fil_cur.cc +++ b/extra/mariabackup/fil_cur.cc @@ -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 @@ read_retry: 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 @@ read_retry: 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: diff --git a/mysql-test/suite/mariabackup/encrypted_page_compressed.opt b/mysql-test/suite/mariabackup/encrypted_page_compressed.opt new file mode 100644 index 00000000000..e5a02a1a1c9 --- /dev/null +++ b/mysql-test/suite/mariabackup/encrypted_page_compressed.opt @@ -0,0 +1,6 @@ +--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 diff --git a/mysql-test/suite/mariabackup/encrypted_page_compressed.result b/mysql-test/suite/mariabackup/encrypted_page_compressed.result new file mode 100644 index 00000000000..92ad84fc04a --- /dev/null +++ b/mysql-test/suite/mariabackup/encrypted_page_compressed.result @@ -0,0 +1,6 @@ +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; diff --git a/mysql-test/suite/mariabackup/encrypted_page_compressed.test b/mysql-test/suite/mariabackup/encrypted_page_compressed.test new file mode 100644 index 00000000000..eaca762d459 --- /dev/null +++ b/mysql-test/suite/mariabackup/encrypted_page_compressed.test @@ -0,0 +1,47 @@ +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; diff --git a/mysql-test/suite/mariabackup/unencrypted_page_compressed.result b/mysql-test/suite/mariabackup/unencrypted_page_compressed.result new file mode 100644 index 00000000000..b4cb27d307b --- /dev/null +++ b/mysql-test/suite/mariabackup/unencrypted_page_compressed.result @@ -0,0 +1,6 @@ +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; diff --git a/mysql-test/suite/mariabackup/unencrypted_page_compressed.test b/mysql-test/suite/mariabackup/unencrypted_page_compressed.test new file mode 100644 index 00000000000..48df75bc5c2 --- /dev/null +++ b/mysql-test/suite/mariabackup/unencrypted_page_compressed.test @@ -0,0 +1,46 @@ +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; diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 11661cf2c40..af68f894aca 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -452,6 +452,8 @@ decompress_with_slot: 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 @@ decompress_with_slot: 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 { From 1b471face8110b205b9bfb460c839127bc6862dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 18 Dec 2018 16:24:52 +0200 Subject: [PATCH 2/2] MDEV-18025: Apply the fix to XtraDB and adjust tests The fix was accidentally only applied to InnoDB, and encryption tests were not adjusted. --- mysql-test/suite/encryption/r/innodb-bad-key-change.result | 1 + .../suite/encryption/r/innodb-encryption-disable.result | 1 + mysql-test/suite/encryption/r/innodb-missing-key.result | 1 + mysql-test/suite/encryption/t/innodb-bad-key-change.test | 1 + mysql-test/suite/encryption/t/innodb-encryption-disable.test | 1 + mysql-test/suite/encryption/t/innodb-missing-key.test | 1 + storage/xtradb/buf/buf0buf.cc | 5 +++-- 7 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/encryption/r/innodb-bad-key-change.result b/mysql-test/suite/encryption/r/innodb-bad-key-change.result index 51fc7a8cbc4..481dbcb285d 100644 --- a/mysql-test/suite/encryption/r/innodb-bad-key-change.result +++ b/mysql-test/suite/encryption/r/innodb-bad-key-change.result @@ -1,6 +1,7 @@ call mtr.add_suppression("Plugin 'file_key_management' init function returned error"); call mtr.add_suppression("Plugin 'file_key_management' registration.*failed"); call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[0-9]+\\] in file '.*test.t[12]\\.ibd' cannot be decrypted\\."); +call mtr.add_suppression("InnoDB: Encrypted page [1-9][0-9]*:3 in file .*test.t1.ibd looks corrupted; key_version=1"); call mtr.add_suppression("mysqld: File .*keysbad3.txt' not found "); # Start server with keys2.txt diff --git a/mysql-test/suite/encryption/r/innodb-encryption-disable.result b/mysql-test/suite/encryption/r/innodb-encryption-disable.result index 4d15098d936..6b6aee2c7e3 100644 --- a/mysql-test/suite/encryption/r/innodb-encryption-disable.result +++ b/mysql-test/suite/encryption/r/innodb-encryption-disable.result @@ -1,4 +1,5 @@ call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[15]\\.ibd' cannot be decrypted\\."); +call mtr.add_suppression("InnoDB: Encrypted page [1-9][0-9]*:3 in file .*test.t[15].ibd looks corrupted; key_version=1"); call mtr.add_suppression("Couldn't load plugins from 'file_key_management*"); create table t5 ( `intcol1` int(32) DEFAULT NULL, diff --git a/mysql-test/suite/encryption/r/innodb-missing-key.result b/mysql-test/suite/encryption/r/innodb-missing-key.result index 53ca0845c25..45a01f03c2b 100644 --- a/mysql-test/suite/encryption/r/innodb-missing-key.result +++ b/mysql-test/suite/encryption/r/innodb-missing-key.result @@ -1,4 +1,5 @@ call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[123]\\.ibd' cannot be decrypted\\."); +call mtr.add_suppression("InnoDB: Encrypted page [1-9][0-9]*:3 in file .*test.t[12].ibd looks corrupted; key_version=1"); # Start server with keys2.txt CREATE TABLE t1(a int not null primary key auto_increment, b varchar(128)) engine=innodb ENCRYPTED=YES ENCRYPTION_KEY_ID=19; diff --git a/mysql-test/suite/encryption/t/innodb-bad-key-change.test b/mysql-test/suite/encryption/t/innodb-bad-key-change.test index 9d61770f543..46a6d2fa27f 100644 --- a/mysql-test/suite/encryption/t/innodb-bad-key-change.test +++ b/mysql-test/suite/encryption/t/innodb-bad-key-change.test @@ -11,6 +11,7 @@ call mtr.add_suppression("Plugin 'file_key_management' init function returned error"); call mtr.add_suppression("Plugin 'file_key_management' registration.*failed"); call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[0-9]+\\] in file '.*test.t[12]\\.ibd' cannot be decrypted\\."); +call mtr.add_suppression("InnoDB: Encrypted page [1-9][0-9]*:3 in file .*test.t1.ibd looks corrupted; key_version=1"); call mtr.add_suppression("mysqld: File .*keysbad3.txt' not found "); diff --git a/mysql-test/suite/encryption/t/innodb-encryption-disable.test b/mysql-test/suite/encryption/t/innodb-encryption-disable.test index 6ade8d6e3b2..4e6147f7745 100644 --- a/mysql-test/suite/encryption/t/innodb-encryption-disable.test +++ b/mysql-test/suite/encryption/t/innodb-encryption-disable.test @@ -8,6 +8,7 @@ # call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[15]\\.ibd' cannot be decrypted\\."); +call mtr.add_suppression("InnoDB: Encrypted page [1-9][0-9]*:3 in file .*test.t[15].ibd looks corrupted; key_version=1"); # Suppression for builds where file_key_management plugin is linked statically call mtr.add_suppression("Couldn't load plugins from 'file_key_management*"); diff --git a/mysql-test/suite/encryption/t/innodb-missing-key.test b/mysql-test/suite/encryption/t/innodb-missing-key.test index 6ea0528825b..c68604fdcf5 100644 --- a/mysql-test/suite/encryption/t/innodb-missing-key.test +++ b/mysql-test/suite/encryption/t/innodb-missing-key.test @@ -8,6 +8,7 @@ # call mtr.add_suppression("InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=[1-9][0-9]*\\] in file '.*test.t[123]\\.ibd' cannot be decrypted\\."); +call mtr.add_suppression("InnoDB: Encrypted page [1-9][0-9]*:3 in file .*test.t[12].ibd looks corrupted; key_version=1"); --echo --echo # Start server with keys2.txt diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc index f23e51fb00a..85b337f641a 100644 --- a/storage/xtradb/buf/buf0buf.cc +++ b/storage/xtradb/buf/buf0buf.cc @@ -482,6 +482,8 @@ decompress_with_slot: 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, @@ -490,7 +492,7 @@ decompress_with_slot: 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) { @@ -4794,7 +4796,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 {