MDEV-10977: [ERROR] InnoDB: Block in space_id 0 in file ibdata1 encrypted.

MDEV-10394: Innodb system table space corrupted

Analysis: After we have read the page in buf_page_io_complete try to
find if the page is encrypted or corrupted. Encryption was determined
by reading FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION field from FIL-header
as a key_version. However, this field is not always zero even when
encryption is not used. Thus, incorrect key_version could lead situation where
decryption is tried to page that is not encrypted.

Fix: We still read key_version information from FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
field but also check if tablespace has encryption information before trying
encrypt the page.
This commit is contained in:
Jan Lindström 2016-10-27 08:18:14 +03:00
parent c1bbedbd4a
commit bc323727de
2 changed files with 248 additions and 115 deletions
storage
innobase/buf
xtradb/buf

View file

@ -526,6 +526,14 @@ buf_page_is_checksum_valid_crc32(
{
ib_uint32_t crc32 = buf_calc_page_crc32(read_buf);
#ifdef UNIV_DEBUG_LEVEL2
if (!(checksum_field1 == crc32 && checksum_field2 == crc32)) {
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum crc32 not valid field1 %lu field2 %lu crc32 %lu.",
checksum_field1, checksum_field2, crc32);
}
#endif
return(checksum_field1 == crc32 && checksum_field2 == crc32);
}
@ -553,6 +561,13 @@ 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)) {
#ifdef UNIV_DEBUG_LEVEL2
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN)
);
#endif
return(false);
}
@ -563,6 +578,13 @@ buf_page_is_checksum_valid_innodb(
if (checksum_field1 != 0
&& checksum_field1 != buf_calc_page_new_checksum(read_buf)) {
#ifdef UNIV_DEBUG_LEVEL2
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN)
);
#endif
return(false);
}
@ -581,6 +603,16 @@ buf_page_is_checksum_valid_none(
ulint checksum_field1,
ulint checksum_field2)
{
#ifdef UNIV_DEBUG_LEVEL2
if (!(checksum_field1 == checksum_field2 || checksum_field1 == BUF_NO_CHECKSUM_MAGIC)) {
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum none not valid field1 %lu field2 %lu crc32 %lu lsn %lu.",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN)
);
}
#endif
return(checksum_field1 == checksum_field2
&& checksum_field1 == BUF_NO_CHECKSUM_MAGIC);
}
@ -598,9 +630,21 @@ buf_page_is_corrupted(
ulint zip_size) /*!< in: size of compressed page;
0 for uncompressed pages */
{
ulint page_encrypted = fil_page_is_encrypted(read_buf);
ulint checksum_field1;
ulint checksum_field2;
ulint space_id = mach_read_from_4(
read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id);
bool page_encrypted = false;
/* Page is encrypted if encryption information is found from
tablespace and page contains used key_version. This is true
also for pages first compressed and then encrypted. */
if (crypt_data &&
crypt_data->type != CRYPT_SCHEME_UNENCRYPTED &&
fil_page_is_encrypted(read_buf)) {
page_encrypted = true;
}
if (!page_encrypted && !zip_size
&& memcmp(read_buf + FIL_PAGE_LSN + 4,
@ -610,6 +654,11 @@ buf_page_is_corrupted(
/* Stored log sequence numbers at the start and the end
of page do not match */
ib_logf(IB_LOG_LEVEL_INFO,
"Log sequence number at the start %lu and the end %lu do not match.",
mach_read_from_4(read_buf + FIL_PAGE_LSN + 4),
mach_read_from_4(read_buf + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4));
return(TRUE);
}
@ -676,6 +725,9 @@ buf_page_is_corrupted(
/* make sure that the page is really empty */
for (ulint i = 0; i < UNIV_PAGE_SIZE; i++) {
if (read_buf[i] != 0) {
ib_logf(IB_LOG_LEVEL_INFO,
"Checksum fields zero but page is not empty.");
return(TRUE);
}
}
@ -686,7 +738,7 @@ buf_page_is_corrupted(
DBUG_EXECUTE_IF("buf_page_is_corrupt_failure", return(TRUE); );
ulint page_no = mach_read_from_4(read_buf + FIL_PAGE_OFFSET);
ulint space_id = mach_read_from_4(read_buf + FIL_PAGE_SPACE_ID);
const srv_checksum_algorithm_t curr_algo =
static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm);
@ -4531,7 +4583,7 @@ buf_page_io_complete(
if (io_type == BUF_IO_READ) {
ulint read_page_no;
ulint read_space_id;
byte* frame;
byte* frame = NULL;
if (!buf_page_decrypt_after_read(bpage)) {
/* encryption error! */
@ -4540,7 +4592,8 @@ buf_page_io_complete(
} else {
frame = ((buf_block_t*) bpage)->frame;
}
goto corrupt;
goto database_corrupted;
}
if (buf_page_get_zip_size(bpage)) {
@ -4551,7 +4604,7 @@ buf_page_io_complete(
FALSE)) {
buf_pool->n_pend_unzip--;
goto corrupt;
goto database_corrupted;
}
buf_pool->n_pend_unzip--;
} else {
@ -4614,7 +4667,7 @@ buf_page_io_complete(
}
goto page_not_corrupt;
;);
corrupt:
database_corrupted:
bool corrupted = buf_page_check_corrupt(bpage);
if (corrupted) {
@ -6177,6 +6230,19 @@ buf_page_decrypt_after_read(
bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame);
buf_pool_t* buf_pool = buf_pool_from_bpage(bpage);
bool success = true;
ulint space_id = mach_read_from_4(
dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id);
/* Page is encrypted if encryption information is found from
tablespace and page contains used key_version. This is true
also for pages first compressed and then encrypted. */
if (!crypt_data ||
(crypt_data &&
crypt_data->type == CRYPT_SCHEME_UNENCRYPTED &&
key_version != 0)) {
key_version = 0;
}
/* If page is encrypted read post-encryption checksum */
if (!page_compressed_encrypted && key_version != 0) {

View file

@ -595,6 +595,14 @@ buf_page_is_checksum_valid_crc32(
{
ib_uint32_t crc32 = buf_calc_page_crc32(read_buf);
#ifdef UNIV_DEBUG_LEVEL2
if (!(checksum_field1 == crc32 && checksum_field2 == crc32)) {
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum crc32 not valid field1 %lu field2 %lu crc32 %lu.",
checksum_field1, checksum_field2, crc32);
}
#endif
return(checksum_field1 == crc32 && checksum_field2 == crc32);
}
@ -622,6 +630,13 @@ 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)) {
#ifdef UNIV_DEBUG_LEVEL2
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN)
);
#endif
return(false);
}
@ -632,6 +647,13 @@ buf_page_is_checksum_valid_innodb(
if (checksum_field1 != 0
&& checksum_field1 != buf_calc_page_new_checksum(read_buf)) {
#ifdef UNIV_DEBUG_LEVEL2
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum innodb not valid field1 %lu field2 %lu crc32 %lu lsn %lu.",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN)
);
#endif
return(false);
}
@ -650,6 +672,16 @@ buf_page_is_checksum_valid_none(
ulint checksum_field1,
ulint checksum_field2)
{
#ifdef UNIV_DEBUG_LEVEL2
if (!(checksum_field1 == checksum_field2 || checksum_field1 == BUF_NO_CHECKSUM_MAGIC)) {
ib_logf(IB_LOG_LEVEL_INFO,
"Page checksum none not valid field1 %lu field2 %lu crc32 %lu lsn %lu.",
checksum_field1, checksum_field2, buf_calc_page_old_checksum(read_buf),
mach_read_from_4(read_buf + FIL_PAGE_LSN)
);
}
#endif
return(checksum_field1 == checksum_field2
&& checksum_field1 == BUF_NO_CHECKSUM_MAGIC);
}
@ -667,9 +699,21 @@ buf_page_is_corrupted(
ulint zip_size) /*!< in: size of compressed page;
0 for uncompressed pages */
{
ulint page_encrypted = fil_page_is_encrypted(read_buf);
ulint checksum_field1;
ulint checksum_field2;
ulint space_id = mach_read_from_4(
read_buf + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id);
bool page_encrypted = false;
/* Page is encrypted if encryption information is found from
tablespace and page contains used key_version. This is true
also for pages first compressed and then encrypted. */
if (crypt_data &&
crypt_data->type != CRYPT_SCHEME_UNENCRYPTED &&
fil_page_is_encrypted(read_buf)) {
page_encrypted = true;
}
if (!page_encrypted && !zip_size
&& memcmp(read_buf + FIL_PAGE_LSN + 4,
@ -679,6 +723,11 @@ buf_page_is_corrupted(
/* Stored log sequence numbers at the start and the end
of page do not match */
ib_logf(IB_LOG_LEVEL_INFO,
"Log sequence number at the start %lu and the end %lu do not match.",
mach_read_from_4(read_buf + FIL_PAGE_LSN + 4),
mach_read_from_4(read_buf + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4));
return(TRUE);
}
@ -745,6 +794,9 @@ buf_page_is_corrupted(
/* make sure that the page is really empty */
for (ulint i = 0; i < UNIV_PAGE_SIZE; i++) {
if (read_buf[i] != 0) {
ib_logf(IB_LOG_LEVEL_INFO,
"Checksum fields zero but page is not empty.");
return(TRUE);
}
}
@ -755,7 +807,7 @@ buf_page_is_corrupted(
DBUG_EXECUTE_IF("buf_page_is_corrupt_failure", return(TRUE); );
ulint page_no = mach_read_from_4(read_buf + FIL_PAGE_OFFSET);
ulint space_id = mach_read_from_4(read_buf + FIL_PAGE_SPACE_ID);
const srv_checksum_algorithm_t curr_algo =
static_cast<srv_checksum_algorithm_t>(srv_checksum_algorithm);
@ -4645,7 +4697,7 @@ buf_page_io_complete(
if (io_type == BUF_IO_READ) {
ulint read_page_no;
ulint read_space_id;
byte* frame;
byte* frame = NULL;
if (!buf_page_decrypt_after_read(bpage)) {
/* encryption error! */
@ -4654,7 +4706,8 @@ buf_page_io_complete(
} else {
frame = ((buf_block_t*) bpage)->frame;
}
goto corrupt;
goto database_corrupted;
}
if (buf_page_get_zip_size(bpage)) {
@ -4666,7 +4719,8 @@ buf_page_io_complete(
os_atomic_decrement_ulint(
&buf_pool->n_pend_unzip, 1);
goto corrupt;
goto database_corrupted;
}
os_atomic_decrement_ulint(&buf_pool->n_pend_unzip, 1);
} else {
@ -4714,119 +4768,119 @@ buf_page_io_complete(
if (UNIV_LIKELY(!bpage->is_corrupt ||
!srv_pass_corrupt_table)) {
/* From version 3.23.38 up we store the page checksum
to the 4 first bytes of the page end lsn field */
/* From version 3.23.38 up we store the page checksum
to the 4 first bytes of the page end lsn field */
if (buf_page_is_corrupted(true, frame,
buf_page_get_zip_size(bpage))) {
if (buf_page_is_corrupted(true, frame,
buf_page_get_zip_size(bpage))) {
/* Not a real corruption if it was triggered by
error injection */
DBUG_EXECUTE_IF("buf_page_is_corrupt_failure",
if (bpage->space > TRX_SYS_SPACE
&& buf_mark_space_corrupt(bpage)) {
ib_logf(IB_LOG_LEVEL_INFO,
"Simulated page corruption");
return(true);
}
goto page_not_corrupt;
;);
corrupt:
bool corrupted = buf_page_check_corrupt(bpage);
if (corrupted) {
fil_system_enter();
space = fil_space_get_by_id(bpage->space);
fil_system_exit();
ib_logf(IB_LOG_LEVEL_ERROR,
"Database page corruption on disk"
" or a failed");
ib_logf(IB_LOG_LEVEL_ERROR,
"Space %u file %s read of page %u.",
bpage->space,
space ? space->name : "NULL",
bpage->offset);
ib_logf(IB_LOG_LEVEL_ERROR,
"You may have to recover"
" from a backup.");
buf_page_print(frame, buf_page_get_zip_size(bpage),
BUF_PAGE_PRINT_NO_CRASH);
ib_logf(IB_LOG_LEVEL_ERROR,
"It is also possible that your operating"
"system has corrupted its own file cache.");
ib_logf(IB_LOG_LEVEL_ERROR,
"and rebooting your computer removes the error.");
ib_logf(IB_LOG_LEVEL_ERROR,
"If the corrupt page is an index page you can also try to");
ib_logf(IB_LOG_LEVEL_ERROR,
"fix the corruption by dumping, dropping, and reimporting");
ib_logf(IB_LOG_LEVEL_ERROR,
"the corrupt table. You can use CHECK");
ib_logf(IB_LOG_LEVEL_ERROR,
"TABLE to scan your table for corruption.");
ib_logf(IB_LOG_LEVEL_ERROR,
"See also "
REFMAN "forcing-innodb-recovery.html"
" about forcing recovery.");
}
if (srv_pass_corrupt_table && bpage->space != 0
&& bpage->space < SRV_LOG_SPACE_FIRST_ID) {
trx_t* trx;
fprintf(stderr,
"InnoDB: space %u will be treated as corrupt.\n",
bpage->space);
fil_space_set_corrupt(bpage->space);
trx = innobase_get_trx();
if (trx && trx->dict_operation_lock_mode == RW_X_LATCH) {
dict_table_set_corrupt_by_space(bpage->space, FALSE);
} else {
dict_table_set_corrupt_by_space(bpage->space, TRUE);
}
bpage->is_corrupt = TRUE;
}
if (srv_force_recovery < SRV_FORCE_IGNORE_CORRUPT) {
/* If page space id is larger than TRX_SYS_SPACE
(0), we will attempt to mark the corresponding
table as corrupted instead of crashing server */
if (bpage->space > TRX_SYS_SPACE
&& buf_mark_space_corrupt(bpage)) {
return(false);
} else {
corrupted = buf_page_check_corrupt(bpage);
if (corrupted) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Ending processing because of a corrupt database page.");
ut_error;
/* Not a real corruption if it was triggered by
error injection */
DBUG_EXECUTE_IF("buf_page_is_corrupt_failure",
if (bpage->space > TRX_SYS_SPACE
&& buf_mark_space_corrupt(bpage)) {
ib_logf(IB_LOG_LEVEL_INFO,
"Simulated page corruption");
return(true);
}
goto page_not_corrupt;
;);
database_corrupted:
ib_push_warning(innobase_get_trx(), DB_DECRYPTION_FAILED,
"Table in tablespace %lu encrypted."
"However key management plugin or used key_id %u is not found or"
" used encryption algorithm or method does not match."
" Can't continue opening the table.",
(ulint)bpage->space, bpage->key_version);
bool corrupted = buf_page_check_corrupt(bpage);
if (bpage->space > TRX_SYS_SPACE) {
if (corrupted) {
buf_mark_space_corrupt(bpage);
}
if (corrupted) {
fil_system_enter();
space = fil_space_get_by_id(bpage->space);
fil_system_exit();
ib_logf(IB_LOG_LEVEL_ERROR,
"Database page corruption on disk"
" or a failed");
ib_logf(IB_LOG_LEVEL_ERROR,
"Space %u file %s read of page %u.",
bpage->space,
space ? space->name : "NULL",
bpage->offset);
ib_logf(IB_LOG_LEVEL_ERROR,
"You may have to recover"
" from a backup.");
buf_page_print(frame, buf_page_get_zip_size(bpage),
BUF_PAGE_PRINT_NO_CRASH);
ib_logf(IB_LOG_LEVEL_ERROR,
"It is also possible that your operating"
"system has corrupted its own file cache.");
ib_logf(IB_LOG_LEVEL_ERROR,
"and rebooting your computer removes the error.");
ib_logf(IB_LOG_LEVEL_ERROR,
"If the corrupt page is an index page you can also try to");
ib_logf(IB_LOG_LEVEL_ERROR,
"fix the corruption by dumping, dropping, and reimporting");
ib_logf(IB_LOG_LEVEL_ERROR,
"the corrupt table. You can use CHECK");
ib_logf(IB_LOG_LEVEL_ERROR,
"TABLE to scan your table for corruption.");
ib_logf(IB_LOG_LEVEL_ERROR,
"See also "
REFMAN "forcing-innodb-recovery.html"
" about forcing recovery.");
}
if (srv_pass_corrupt_table && bpage->space != 0
&& bpage->space < SRV_LOG_SPACE_FIRST_ID) {
trx_t* trx;
fprintf(stderr,
"InnoDB: space %u will be treated as corrupt.\n",
bpage->space);
fil_space_set_corrupt(bpage->space);
trx = innobase_get_trx();
if (trx && trx->dict_operation_lock_mode == RW_X_LATCH) {
dict_table_set_corrupt_by_space(bpage->space, FALSE);
} else {
ut_error;
dict_table_set_corrupt_by_space(bpage->space, TRUE);
}
bpage->is_corrupt = TRUE;
}
if (srv_force_recovery < SRV_FORCE_IGNORE_CORRUPT) {
/* If page space id is larger than TRX_SYS_SPACE
(0), we will attempt to mark the corresponding
table as corrupted instead of crashing server */
if (bpage->space > TRX_SYS_SPACE
&& buf_mark_space_corrupt(bpage)) {
return(false);
} else {
corrupted = buf_page_check_corrupt(bpage);
if (corrupted) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Ending processing because of a corrupt database page.");
ut_error;
}
ib_push_warning(innobase_get_trx(), DB_DECRYPTION_FAILED,
"Table in tablespace %lu encrypted."
"However key management plugin or used key_id %u is not found or"
" used encryption algorithm or method does not match."
" Can't continue opening the table.",
(ulint)bpage->space, bpage->key_version);
if (bpage->space > TRX_SYS_SPACE) {
if (corrupted) {
buf_mark_space_corrupt(bpage);
}
} else {
ut_error;
}
return(false);
}
return(false);
}
}
}
} /**/
DBUG_EXECUTE_IF("buf_page_is_corrupt_failure",
@ -6374,6 +6428,19 @@ buf_page_decrypt_after_read(
bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame);
buf_pool_t* buf_pool = buf_pool_from_bpage(bpage);
bool success = true;
ulint space_id = mach_read_from_4(
dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id);
/* Page is encrypted if encryption information is found from
tablespace and page contains used key_version. This is true
also for pages first compressed and then encrypted. */
if (!crypt_data ||
(crypt_data &&
crypt_data->type == CRYPT_SCHEME_UNENCRYPTED &&
key_version != 0)) {
key_version = 0;
}
/* If page is encrypted read post-encryption checksum */
if (!page_compressed_encrypted && key_version != 0) {