Commit 03400676 authored by Jan Lindström's avatar Jan Lindström

After review fixes for MDEV-11759.

buf_page_is_checksum_valid_crc32()
buf_page_is_checksum_valid_innodb()
buf_page_is_checksum_valid_none():
	Use ULINTPF instead of %lu and %u for ib_uint32_t

fil_space_verify_crypt_checksum():
	Check that page is really empty if checksum and
	LSN are zero.

fil_space_verify_crypt_checksum():
	Correct the comment to be more agurate.

buf0buf.h:
	Remove unnecessary is_corrupt variable from
	buf_page_t structure.
parent 9017a05d
...@@ -12,22 +12,14 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r ...@@ -12,22 +12,14 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r
CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK"); CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK");
CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption."); CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption.");
CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery."); CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery.");
flush tables;
# Create and populate the table to be corrupted # Create and populate the table to be corrupted
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB; CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB;
INSERT INTO t1 (b) VALUES ('corrupt me'); INSERT INTO t1 (b) VALUES ('corrupt me');
INSERT INTO t1 (b) VALUES ('corrupt me'); INSERT INTO t1 (b) VALUES ('corrupt me');
# Write file to make mysql-test-run.pl expect the "crash", but don't
# start it until it's told to
# We give 30 seconds to do a clean shutdown because we do not want
# to redo apply the pages of t1.ibd at the time of recovery.
# We want SQL to initiate the first access to t1.ibd.
# Wait until disconnected.
# Backup the t1.ibd before corrupting # Backup the t1.ibd before corrupting
# Corrupt the table # Corrupt the table
Munged a string. Munged a string.
Munged a string. Munged a string.
# Write file to make mysql-test-run.pl start up the server again
SET DEBUG_DBUG = '+d,innodb_page_corruption_retries'; SET DEBUG_DBUG = '+d,innodb_page_corruption_retries';
# Write file to make mysql-test-run.pl expect the "crash", but don't # Write file to make mysql-test-run.pl expect the "crash", but don't
# start it until it's told to # start it until it's told to
...@@ -36,6 +28,5 @@ SET DEBUG_DBUG = '+d,innodb_page_corruption_retries'; ...@@ -36,6 +28,5 @@ SET DEBUG_DBUG = '+d,innodb_page_corruption_retries';
SELECT * FROM t1; SELECT * FROM t1;
ERROR HY000: Lost connection to MySQL server during query ERROR HY000: Lost connection to MySQL server during query
# Restore the original t1.ibd # Restore the original t1.ibd
# Write file to make mysql-test-run.pl start up the server again
# Cleanup # Cleanup
DROP TABLE t1; DROP TABLE t1;
...@@ -33,8 +33,6 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r ...@@ -33,8 +33,6 @@ CALL mtr.add_suppression("InnoDB: fix the corruption by dumping, dropping, and r
CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK"); CALL mtr.add_suppression("InnoDB: the corrupt table. You can use CHECK");
CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption."); CALL mtr.add_suppression("InnoDB: TABLE to scan your table for corruption.");
CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery."); CALL mtr.add_suppression("InnoDB: See also .* about forcing recovery.");
flush tables;
--echo # Create and populate the table to be corrupted --echo # Create and populate the table to be corrupted
CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB; CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b TEXT) ENGINE=InnoDB;
...@@ -52,16 +50,9 @@ INSERT INTO t1 (b) VALUES ('corrupt me'); ...@@ -52,16 +50,9 @@ INSERT INTO t1 (b) VALUES ('corrupt me');
let $MYSQLD_DATADIR=`select @@datadir`; let $MYSQLD_DATADIR=`select @@datadir`;
let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd; let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd;
--echo # Write file to make mysql-test-run.pl expect the "crash", but don't
--echo # start it until it's told to
--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--echo # We give 30 seconds to do a clean shutdown because we do not want --source include/shutdown_mysqld.inc
--echo # to redo apply the pages of t1.ibd at the time of recovery.
--echo # We want SQL to initiate the first access to t1.ibd.
shutdown_server 30;
--echo # Wait until disconnected.
--source include/wait_until_disconnected.inc --source include/wait_until_disconnected.inc
--echo # Backup the t1.ibd before corrupting --echo # Backup the t1.ibd before corrupting
...@@ -94,10 +85,7 @@ while ($len = sysread IBD_FILE, $chunk, 1024) ...@@ -94,10 +85,7 @@ while ($len = sysread IBD_FILE, $chunk, 1024)
close IBD_FILE; close IBD_FILE;
EOF EOF
--echo # Write file to make mysql-test-run.pl start up the server again --source include/start_mysqld.inc
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc
SET DEBUG_DBUG = '+d,innodb_page_corruption_retries'; SET DEBUG_DBUG = '+d,innodb_page_corruption_retries';
...@@ -119,10 +107,7 @@ SLEEP 1; ...@@ -119,10 +107,7 @@ SLEEP 1;
--remove_file $MYSQLD_DATADIR/test/t1.ibd --remove_file $MYSQLD_DATADIR/test/t1.ibd
--move_file $MYSQLD_DATADIR/test/t1.ibd.backup $MYSQLD_DATADIR/test/t1.ibd --move_file $MYSQLD_DATADIR/test/t1.ibd.backup $MYSQLD_DATADIR/test/t1.ibd
--echo # Write file to make mysql-test-run.pl start up the server again --source include/start_mysqld.inc
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc
# Note SET DEBUG = '-d,innodb_page_corruption_retries' is not required # Note SET DEBUG = '-d,innodb_page_corruption_retries' is not required
# because the session information is lost after server restart # because the session information is lost after server restart
......
...@@ -520,8 +520,9 @@ buf_page_is_checksum_valid_crc32( ...@@ -520,8 +520,9 @@ buf_page_is_checksum_valid_crc32(
if (!(checksum_field1 == crc32 && checksum_field2 == crc32)) { if (!(checksum_field1 == crc32 && checksum_field2 == crc32)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum crc32 not valid field1 %lu field2 %lu crc32 %lu.", ("Page checksum crc32 not valid field1 " ULINTPF
checksum_field1, checksum_field2, (ulint)crc32)); " field2 " ULINTPF " crc32 %u.",
checksum_field1, checksum_field2, crc32));
} }
...@@ -553,7 +554,8 @@ buf_page_is_checksum_valid_innodb( ...@@ -553,7 +554,8 @@ buf_page_is_checksum_valid_innodb(
if (checksum_field2 != mach_read_from_4(read_buf + FIL_PAGE_LSN) if (checksum_field2 != mach_read_from_4(read_buf + FIL_PAGE_LSN)
&& checksum_field2 != buf_calc_page_old_checksum(read_buf)) { && checksum_field2 != buf_calc_page_old_checksum(read_buf)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.", ("Page checksum innodb not valid field1 " ULINTPF
" field2 " ULINTPF "crc32 " ULINTPF " lsn " ULINTPF ".",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf), checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN))); mach_read_from_4(read_buf + FIL_PAGE_LSN)));
...@@ -568,7 +570,8 @@ buf_page_is_checksum_valid_innodb( ...@@ -568,7 +570,8 @@ buf_page_is_checksum_valid_innodb(
if (checksum_field1 != 0 if (checksum_field1 != 0
&& checksum_field1 != buf_calc_page_new_checksum(read_buf)) { && checksum_field1 != buf_calc_page_new_checksum(read_buf)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.", ("Page checksum innodb not valid field1 " ULINTPF
" field2 " ULINTPF "crc32 " ULINTPF " lsn " ULINTPF ".",
checksum_field1, checksum_field2, buf_calc_page_new_checksum(read_buf), checksum_field1, checksum_field2, buf_calc_page_new_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN))); mach_read_from_4(read_buf + FIL_PAGE_LSN)));
...@@ -593,7 +596,8 @@ buf_page_is_checksum_valid_none( ...@@ -593,7 +596,8 @@ buf_page_is_checksum_valid_none(
if (!(checksum_field1 == checksum_field2 && checksum_field1 == BUF_NO_CHECKSUM_MAGIC)) { if (!(checksum_field1 == checksum_field2 && checksum_field1 == BUF_NO_CHECKSUM_MAGIC)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum none not valid field1 %lu field2 %lu crc32 %lu lsn %lu.", ("Page checksum none not valid field1 " ULINTPF
" field2 " ULINTPF "crc32 " ULINTPF " lsn " ULINTPF ".",
checksum_field1, checksum_field2, BUF_NO_CHECKSUM_MAGIC, checksum_field1, checksum_field2, BUF_NO_CHECKSUM_MAGIC,
mach_read_from_4(read_buf + FIL_PAGE_LSN))); mach_read_from_4(read_buf + FIL_PAGE_LSN)));
} }
......
...@@ -981,8 +981,9 @@ fil_space_verify_crypt_checksum( ...@@ -981,8 +981,9 @@ fil_space_verify_crypt_checksum(
/* Declare empty pages non-corrupted */ /* Declare empty pages non-corrupted */
if (checksum == 0 if (checksum == 0
&& *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_LSN) == 0) { && *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_LSN) == 0
return (true); && buf_page_is_zeroes(page, zip_size)) {
return(true);
} }
/* Compressed and encrypted pages do not have checksum. Assume not /* Compressed and encrypted pages do not have checksum. Assume not
...@@ -1016,16 +1017,31 @@ fil_space_verify_crypt_checksum( ...@@ -1016,16 +1017,31 @@ fil_space_verify_crypt_checksum(
bool encrypted = (checksum == cchecksum1 || checksum == cchecksum2 bool encrypted = (checksum == cchecksum1 || checksum == cchecksum2
|| checksum == BUF_NO_CHECKSUM_MAGIC); || checksum == BUF_NO_CHECKSUM_MAGIC);
/* Old InnoDB versions did not initialize /* MySQL 5.6 and MariaDB 10.0 and 10.1 will write an LSN to the
FIL_PAGE_FILE_FLUSH_LSN field so there could be garbage first page of each system tablespace file at
and above checksum check could produce false positive. FIL_PAGE_FILE_FLUSH_LSN offset. On other pages and in other files,
Thus we also check does the traditional stored the field might have been uninitialized until MySQL 5.5. In MySQL 5.7
checksum fields match the calculated one. Both of these (and MariaDB Server 10.2.2) WL#7990 stopped writing the field for other
could naturally produce false positive but then than page 0 of the system tablespace.
we just decrypt the page and after that corrupted
pages very probable stay corrupted and valid Starting from MariaDB 10.1 the field has been repurposed for
pages very probable stay valid. encryption key_version.
Starting with MySQL 5.7 (and MariaDB Server 10.2), the
field has been repurposed for SPATIAL INDEX pages for
FIL_RTREE_SPLIT_SEQ_NUM.
Note that FIL_PAGE_FILE_FLUSH_LSN is not included in the InnoDB page
checksum.
Thus, FIL_PAGE_FILE_FLUSH_LSN could contain any value. While the
field would usually be 0 for pages that are not encrypted, we cannot
assume that a nonzero value means that the page is encrypted.
Therefore we must validate the page both as encrypted and unencrypted
when FIL_PAGE_FILE_FLUSH_LSN does not contain 0.
*/ */
ulint checksum1 = mach_read_from_4( ulint checksum1 = mach_read_from_4(
page + FIL_PAGE_SPACE_OR_CHKSUM); page + FIL_PAGE_SPACE_OR_CHKSUM);
......
...@@ -1759,7 +1759,7 @@ struct buf_page_t{ ...@@ -1759,7 +1759,7 @@ struct buf_page_t{
0 if the block was never accessed 0 if the block was never accessed
in the buffer pool. Protected by in the buffer pool. Protected by
block mutex */ block mutex */
ibool is_corrupt;
# if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG # if defined UNIV_DEBUG_FILE_ACCESSES || defined UNIV_DEBUG
ibool file_page_was_freed; ibool file_page_was_freed;
/*!< this is set to TRUE when /*!< this is set to TRUE when
......
...@@ -585,8 +585,9 @@ buf_page_is_checksum_valid_crc32( ...@@ -585,8 +585,9 @@ buf_page_is_checksum_valid_crc32(
if (!(checksum_field1 == crc32 && checksum_field2 == crc32)) { if (!(checksum_field1 == crc32 && checksum_field2 == crc32)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum crc32 not valid field1 %lu field2 %lu crc32 %lu.", ("Page checksum crc32 not valid field1 " ULINTPF
checksum_field1, checksum_field2, (ulint)crc32)); " field2 " ULINTPF " crc32 %u.",
checksum_field1, checksum_field2, crc32));
} }
return(checksum_field1 == crc32 && checksum_field2 == crc32); return(checksum_field1 == crc32 && checksum_field2 == crc32);
...@@ -618,7 +619,8 @@ buf_page_is_checksum_valid_innodb( ...@@ -618,7 +619,8 @@ buf_page_is_checksum_valid_innodb(
&& checksum_field2 != buf_calc_page_old_checksum(read_buf)) { && checksum_field2 != buf_calc_page_old_checksum(read_buf)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.", ("Page checksum innodb not valid field1 " ULINTPF
" field2 " ULINTPF "crc32 " ULINTPF " lsn " ULINTPF ".",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf), checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN))); mach_read_from_4(read_buf + FIL_PAGE_LSN)));
...@@ -634,7 +636,8 @@ buf_page_is_checksum_valid_innodb( ...@@ -634,7 +636,8 @@ buf_page_is_checksum_valid_innodb(
&& checksum_field1 != buf_calc_page_new_checksum(read_buf)) { && checksum_field1 != buf_calc_page_new_checksum(read_buf)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.", ("Page checksum innodb not valid field1 " ULINTPF
" field2 " ULINTPF "crc32 " ULINTPF " lsn " ULINTPF ".",
checksum_field1, checksum_field2, buf_calc_page_new_checksum(read_buf), checksum_field1, checksum_field2, buf_calc_page_new_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN))); mach_read_from_4(read_buf + FIL_PAGE_LSN)));
...@@ -659,7 +662,8 @@ buf_page_is_checksum_valid_none( ...@@ -659,7 +662,8 @@ buf_page_is_checksum_valid_none(
if (!(checksum_field1 == checksum_field2 && checksum_field1 == BUF_NO_CHECKSUM_MAGIC)) { if (!(checksum_field1 == checksum_field2 && checksum_field1 == BUF_NO_CHECKSUM_MAGIC)) {
DBUG_PRINT("buf_checksum", DBUG_PRINT("buf_checksum",
("Page checksum none not valid field1 %lu field2 %lu crc32 %lu lsn %lu.", ("Page checksum none not valid field1 " ULINTPF
" field2 " ULINTPF "crc32 " ULINTPF " lsn " ULINTPF ".",
checksum_field1, checksum_field2, BUF_NO_CHECKSUM_MAGIC, checksum_field1, checksum_field2, BUF_NO_CHECKSUM_MAGIC,
mach_read_from_4(read_buf + FIL_PAGE_LSN))); mach_read_from_4(read_buf + FIL_PAGE_LSN)));
} }
......
...@@ -981,8 +981,9 @@ fil_space_verify_crypt_checksum( ...@@ -981,8 +981,9 @@ fil_space_verify_crypt_checksum(
/* Declare empty pages non-corrupted */ /* Declare empty pages non-corrupted */
if (checksum == 0 if (checksum == 0
&& *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_LSN) == 0) { && *reinterpret_cast<const ib_uint64_t*>(page + FIL_PAGE_LSN) == 0
return (true); && buf_page_is_zeroes(page, zip_size)) {
return(true);
} }
/* Compressed and encrypted pages do not have checksum. Assume not /* Compressed and encrypted pages do not have checksum. Assume not
...@@ -1016,16 +1017,30 @@ fil_space_verify_crypt_checksum( ...@@ -1016,16 +1017,30 @@ fil_space_verify_crypt_checksum(
bool encrypted = (checksum == cchecksum1 || checksum == cchecksum2 bool encrypted = (checksum == cchecksum1 || checksum == cchecksum2
|| checksum == BUF_NO_CHECKSUM_MAGIC); || checksum == BUF_NO_CHECKSUM_MAGIC);
/* Old InnoDB versions did not initialize /* MySQL 5.6 and MariaDB 10.0 and 10.1 will write an LSN to the
FIL_PAGE_FILE_FLUSH_LSN field so there could be garbage first page of each system tablespace file at
and above checksum check could produce false positive. FIL_PAGE_FILE_FLUSH_LSN offset. On other pages and in other files,
Thus we also check does the traditional stored the field might have been uninitialized until MySQL 5.5. In MySQL 5.7
checksum fields match the calculated one. Both of these (and MariaDB Server 10.2.2) WL#7990 stopped writing the field for other
could naturally produce false positive but then than page 0 of the system tablespace.
we just decrypt the page and after that corrupted
pages very probable stay corrupted and valid Starting from MariaDB 10.1 the field has been repurposed for
pages very probable stay valid. encryption key_version.
Starting with MySQL 5.7 (and MariaDB Server 10.2), the
field has been repurposed for SPATIAL INDEX pages for
FIL_RTREE_SPLIT_SEQ_NUM.
Note that FIL_PAGE_FILE_FLUSH_LSN is not included in the InnoDB page
checksum.
Thus, FIL_PAGE_FILE_FLUSH_LSN could contain any value. While the
field would usually be 0 for pages that are not encrypted, we cannot
assume that a nonzero value means that the page is encrypted.
Therefore we must validate the page both as encrypted and unencrypted
when FIL_PAGE_FILE_FLUSH_LSN does not contain 0.
*/ */
ulint checksum1 = mach_read_from_4( ulint checksum1 = mach_read_from_4(
page + FIL_PAGE_SPACE_OR_CHKSUM); page + FIL_PAGE_SPACE_OR_CHKSUM);
......
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