mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +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: 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';
|
|||||||
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
|
|||||||
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');
|
|||||||
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)
|
|||||||
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;
|
|||||||
--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(
|
|||||||
|
|
||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
|
|
||||||
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,7 +981,8 @@ 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
|
||||||
|
&& buf_page_is_zeroes(page, zip_size)) {
|
||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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{
|
|||||||
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(
|
|||||||
|
|
||||||
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(
|
|||||||
&& 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(
|
|||||||
&& 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(
|
|||||||
|
|
||||||
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,7 +981,8 @@ 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
|
||||||
|
&& buf_page_is_zeroes(page, zip_size)) {
|
||||||
return(true);
|
return(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user