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