diff options
author | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2019-06-28 18:49:43 +0530 |
---|---|---|
committer | Thirunarayanan Balathandayuthapani <thiru@mariadb.com> | 2019-06-28 18:58:52 +0530 |
commit | e4a0dbfb4aa77a9d41039f5cd7144d98a6b9baed (patch) | |
tree | d2c2344720e5b47345b0aecf642165ec17954f76 | |
parent | f7a4a8719bd59eca27e3e5e467c138853aa63dd5 (diff) | |
download | mariadb-git-e4a0dbfb4aa77a9d41039f5cd7144d98a6b9baed.tar.gz |
MDEV-19781 Add page id matching check in innochecksum tool
Added the condition in innochecksum tool to check page id mismatch.
This could catch the write corruption caused by InnoDB.
Added the debug insert inside fil_io() to check whether it writes
the page to wrong offset.
-rw-r--r-- | extra/innochecksum.cc | 101 | ||||
-rw-r--r-- | mysql-test/suite/encryption/r/innochecksum.result | 2 | ||||
-rw-r--r-- | mysql-test/suite/encryption/t/innochecksum.test | 3 | ||||
-rw-r--r-- | mysql-test/suite/innodb/r/page_id_innochecksum.result | 6 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/page_id_innochecksum.test | 52 | ||||
-rw-r--r-- | storage/innobase/fil/fil0fil.cc | 14 |
6 files changed, 163 insertions, 15 deletions
diff --git a/extra/innochecksum.cc b/extra/innochecksum.cc index 3abc541b028..514058cc3f3 100644 --- a/extra/innochecksum.cc +++ b/extra/innochecksum.cc @@ -100,6 +100,8 @@ ulong srv_page_size; page_size_t univ_page_size(0, 0, false); /* Current page number (0 based). */ unsigned long long cur_page_num; +/* Current space. */ +unsigned long long cur_space; /* Skip the checksum verification. */ static bool no_check; /* Enabled for strict checksum verification. */ @@ -451,6 +453,27 @@ ulong read_file( return bytes; } +/** Check whether the page contains all zeroes. +@param[in] buf page +@param[in] size physical size of the page +@return true if the page is all zeroes; else false */ +static bool is_page_all_zeroes( + byte* buf, + ulint size) +{ + /* On pages that are not all zero, the page number + must match. */ + const ulint* p = reinterpret_cast<const ulint*>(buf); + const ulint* const end = reinterpret_cast<const ulint*>(buf + size); + do { + if (*p++) { + return false; + } + } while (p != end); + + return true; +} + /** Check if page is corrupted or not. @param[in] buf page frame @param[in] page_size page size @@ -462,10 +485,10 @@ ulong read_file( static bool is_page_corrupted( - byte* buf, + byte* buf, const page_size_t& page_size, - bool is_encrypted, - bool is_compressed) + bool is_encrypted, + bool is_compressed) { /* enable if page is corrupted. */ @@ -478,6 +501,24 @@ is_page_corrupted( ulint space_id = mach_read_from_4( buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); + if (mach_read_from_4(buf + FIL_PAGE_OFFSET) != cur_page_num + || space_id != cur_space) { + /* On pages that are not all zero, the page number + must match. */ + if (is_page_all_zeroes(buf, page_size.physical())) { + return false; + } + + if (is_log_enabled) { + fprintf(log_file, + "page id mismatch space::" ULINTPF + " page::%llu \n", + space_id, cur_page_num); + } + + return true; + } + /* We can't trust only a page type, thus we take account also fsp_flags or crypt_data on page 0 */ if ((page_type == FIL_PAGE_PAGE_COMPRESSED && is_compressed) || @@ -1576,9 +1617,6 @@ int main( FILE* fil_page_type = NULL; fpos_t pos; - /* Use to check the space id of given file. If space_id is zero, - then check whether page is doublewrite buffer.*/ - ulint space_id = 0UL; /* enable when space_id of given file is zero. */ bool is_system_tablespace = false; @@ -1700,9 +1738,8 @@ int main( /* enable variable is_system_tablespace when space_id of given file is zero. Use to skip the checksum verification and rewrite for doublewrite pages. */ - is_system_tablespace = (!memcmp(&space_id, buf + - FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, 4)) - ? true : false; + cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID); + cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET); /* Determine page size, zip_size and page compression from fsp_flags and encryption metadata from page 0 */ @@ -1715,7 +1752,9 @@ int main( srv_page_size = page_size.logical(); bool is_compressed = FSP_FLAGS_HAS_PAGE_COMPRESSION(flags); - if (page_size.physical() > UNIV_ZIP_SIZE_MIN) { + if (physical_page_size == UNIV_ZIP_SIZE_MIN) { + partial_page_read = false; + } else { /* Read rest of the page 0 to determine crypt_data */ bytes = ulong(read_file(buf, partial_page_read, page_size.physical(), fil_in)); @@ -1731,6 +1770,7 @@ int main( partial_page_read = false; } + /* Now that we have full page 0 in buffer, check encryption */ bool is_encrypted = check_encryption(filename, page_size, buf); @@ -1741,7 +1781,9 @@ int main( unsigned long long tmp_allow_mismatches = allow_mismatches; allow_mismatches = 0; - exit_status = verify_checksum(buf, page_size, is_encrypted, is_compressed, &mismatch_count); + exit_status = verify_checksum( + buf, page_size, is_encrypted, + is_compressed, &mismatch_count); if (exit_status) { fprintf(stderr, "Error: Page 0 checksum mismatch, can't continue. \n"); @@ -1805,6 +1847,36 @@ int main( } } + off_t cur_offset = 0; + /* Find the first non all-zero page and fetch the + space id from there. */ + while (is_page_all_zeroes(buf, physical_page_size)) { + bytes = ulong(read_file( + buf, false, physical_page_size, + fil_in)); + + if (feof(fil_in)) { + fprintf(stderr, "All are " + "zero-filled pages."); + goto my_exit; + } + + cur_offset++; + } + + cur_space = mach_read_from_4(buf + FIL_PAGE_SPACE_ID); + is_system_tablespace = (cur_space == 0); + + if (cur_offset > 0) { + /* Re-read the non-zero page to check the + checksum. So move the file pointer to + previous position and reset the page number too. */ + cur_page_num = mach_read_from_4(buf + FIL_PAGE_OFFSET); + if (!start_page) { + goto first_non_zero; + } + } + /* seek to the necessary position */ if (start_page) { if (!read_from_stdin) { @@ -1902,6 +1974,7 @@ int main( goto my_exit; } +first_non_zero: if (is_system_tablespace) { /* enable when page is double write buffer.*/ skip_page = is_page_doublewritebuffer(buf); @@ -1922,8 +1995,10 @@ int main( checksum verification.*/ if (!no_check && !skip_page - && (exit_status = verify_checksum(buf, page_size, - is_encrypted, is_compressed, &mismatch_count))) { + && (exit_status = verify_checksum( + buf, page_size, + is_encrypted, is_compressed, + &mismatch_count))) { goto my_exit; } diff --git a/mysql-test/suite/encryption/r/innochecksum.result b/mysql-test/suite/encryption/r/innochecksum.result index 6ea54f3d053..2c1279cd232 100644 --- a/mysql-test/suite/encryption/r/innochecksum.result +++ b/mysql-test/suite/encryption/r/innochecksum.result @@ -33,7 +33,7 @@ CREATE TABLE t6 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB; # Run innochecksum on t2 # Run innochecksum on t3 # Run innochecksum on t6 -# no encryption corrupting the field should not have effect +# Space ID mismatch # Restore the original tables # Corrupt FIL_DATA+10 (data) # Run innochecksum on t2 diff --git a/mysql-test/suite/encryption/t/innochecksum.test b/mysql-test/suite/encryption/t/innochecksum.test index f1c1b65d418..47da8525130 100644 --- a/mysql-test/suite/encryption/t/innochecksum.test +++ b/mysql-test/suite/encryption/t/innochecksum.test @@ -206,7 +206,8 @@ EOF --exec $INNOCHECKSUM $t3_IBD --echo # Run innochecksum on t6 ---echo # no encryption corrupting the field should not have effect +--echo # Space ID mismatch +--error 1 --exec $INNOCHECKSUM $t6_IBD --enable_result_log diff --git a/mysql-test/suite/innodb/r/page_id_innochecksum.result b/mysql-test/suite/innodb/r/page_id_innochecksum.result new file mode 100644 index 00000000000..e2c13442fe6 --- /dev/null +++ b/mysql-test/suite/innodb/r/page_id_innochecksum.result @@ -0,0 +1,6 @@ +# Set the environmental variables +create table t1(f1 int not null)engine=innodb; +insert into t1 values(1), (2), (3); +# Change the page offset +FOUND 1 /page id mismatch/ in result.log +drop table t1; diff --git a/mysql-test/suite/innodb/t/page_id_innochecksum.test b/mysql-test/suite/innodb/t/page_id_innochecksum.test new file mode 100644 index 00000000000..5bd8a58fcd5 --- /dev/null +++ b/mysql-test/suite/innodb/t/page_id_innochecksum.test @@ -0,0 +1,52 @@ +--source include/have_innodb.inc +--echo # Set the environmental variables +let MYSQLD_BASEDIR= `SELECT @@basedir`; +let MYSQLD_DATADIR= `SELECT @@datadir`; +let INNODB_PAGE_SIZE=`select @@innodb_page_size`; + +create table t1(f1 int not null)engine=innodb; +insert into t1 values(1), (2), (3); +let $resultlog=$MYSQLTEST_VARDIR/tmp/result.log; + +--source include/shutdown_mysqld.inc +--echo # Change the page offset +perl; +use strict; +use warnings; +use Fcntl qw(:DEFAULT :seek); +do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl"; + +my $page_size = $ENV{INNODB_PAGE_SIZE}; + +sysopen IBD_FILE, "$ENV{MYSQLD_DATADIR}/test/t1.ibd", O_RDWR +|| die "Cannot open t1.ibd\n"; +sysread(IBD_FILE, $_, 38) || die "Cannot read t1.ibd\n"; +my $space = unpack("x[34]N", $_); +sysseek(IBD_FILE, $page_size * 3, SEEK_SET) || die "Cannot seek t1.ibd\n"; + +my $head = pack("Nx[18]", 4); # better to have a valid page number +my $body = chr(0) x ($page_size - 38 - 8); + +# Calculate innodb_checksum_algorithm=crc32 for the unencrypted page. +# The following bytes are excluded: +# bytes 0..3 (the checksum is stored there) +# bytes 26..37 (encryption key version, post-encryption checksum, tablespace id) +# bytes $page_size-8..$page_size-1 (checksum, LSB of FIL_PAGE_LSN) +my $polynomial = 0x82f63b78; # CRC-32C +my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial); + +my $page= pack("N",$ck).$head.pack("NNN",1,$ck,$space).$body.pack("Nx[4]",$ck); +die unless syswrite(IBD_FILE, $page, $page_size) == $page_size; +close IBD_FILE; +EOF + +--error 1 +exec $INNOCHECKSUM -C crc32 -l $resultlog $MYSQLD_DATADIR/test/t1.ibd; + +let SEARCH_FILE = $MYSQLTEST_VARDIR/tmp/result.log; +let SEARCH_PATTERN=page id mismatch; +--source include/search_pattern_in_file.inc + +--remove_file $resultlog +--source include/start_mysqld.inc +drop table t1; diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 08c3a699a04..f718b8bdc52 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -5056,6 +5056,20 @@ fil_io( req_type.set_fil_node(node); +#ifdef UNIV_DEBUG + if (req_type.is_write() + && page_id.space() != SRV_LOG_SPACE_FIRST_ID + && (page_id.space() != TRX_SYS_SPACE + || buf_dblwr == NULL + || !(page_id.page_no() >= + (buf_dblwr->block1 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE) + || page_id.page_no() >= + (buf_dblwr->block2 + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE)))) { + + ut_ad(offset == page_id.page_no() * page_size.physical()); + } +#endif /* UNIV_DEBUG */ + /* Queue the aio request */ dberr_t err = os_aio( req_type, |