mirror of
https://github.com/MariaDB/server.git
synced 2025-08-09 22:24:09 +03:00
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.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user