Skip to content

Commit 1a780ee

Browse files
committed
MDEV-17958 Make bug-endian innodb_checksum_algorithm=crc32 optional
In MySQL 5.7, it was noticed that files are not portable between big-endian and little-endian processor architectures (such as SPARC and x86), because the original implementation of innodb_checksum_algorithm=crc32 was not byte order agnostic. A byte order agnostic implementation of innodb_checksum_algorithm=crc32 was only added to MySQL 5.7, not backported to 5.6. Consequently, MariaDB Server versions 10.0 and 10.1 only contain the CRC-32C implementation that works incorrectly on big-endian architectures, and MariaDB Server 10.2.2 got the byte-order agnostic CRC-32C implementation from MySQL 5.7. MySQL 5.7 introduced a "legacy crc32" variant that is functionally equivalent to the big-endian version of the original crc32 implementation. Thanks to this variant, old data files can be transferred from big-endian systems to newer versions. Introducing new variants of checksum algorithms (without introducing new names for them, or something on the pages themselves to identify the algorithm) generally is a bad idea, because each checksum algorithm is like a lottery ticket. The more algorithms you try, the more likely it will be for the checksum to match on a corrupted page. So, essentially MySQL 5.7 weakened innodb_checksum_algorithm=crc32, and MariaDB 10.2.2 inherited this weakening. We introduce a build option that together with MDEV-17957 makes innodb_checksum_algorithm=strict_crc32 strict again by only allowing one variant of the checksum to match. WITH_INNODB_BUG_ENDIAN_CRC32: A new cmake option for enabling the bug-compatible "legacy crc32" checksum. This is only enabled on big-endian systems by default, to facilitate an upgrade from MariaDB 10.0 or 10.1. Checked by #ifdef INNODB_BUG_ENDIAN_CRC32. ut_crc32_byte_by_byte: Remove (unused function). legacy_big_endian_checksum: Remove. This variable seems to have unnecessarily complicated the logic. When the weakening is enabled, we must always fall back to the buggy checksum. buf_page_check_crc32(): A helper function to compute one or two CRC-32C variants.
1 parent 2e5aea4 commit 1a780ee

File tree

10 files changed

+211
-253
lines changed

10 files changed

+211
-253
lines changed

storage/innobase/buf/buf0buf.cc

Lines changed: 59 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -756,17 +756,14 @@ buf_page_is_zeroes(
756756
@param[in] read_buf database page
757757
@param[in] checksum_field1 new checksum field
758758
@param[in] checksum_field2 old checksum field
759-
@param[in] use_legacy_big_endian use legacy big endian algorithm
760759
@return true if the page is in crc32 checksum format. */
761760
bool
762761
buf_page_is_checksum_valid_crc32(
763762
const byte* read_buf,
764763
ulint checksum_field1,
765-
ulint checksum_field2,
766-
bool use_legacy_big_endian)
764+
ulint checksum_field2)
767765
{
768-
const uint32_t crc32 = buf_calc_page_crc32(read_buf,
769-
use_legacy_big_endian);
766+
const uint32_t crc32 = buf_calc_page_crc32(read_buf);
770767

771768
#ifdef UNIV_INNOCHECKSUM
772769
if (log_file
@@ -783,17 +780,11 @@ buf_page_is_checksum_valid_crc32(
783780
return false;
784781
}
785782

786-
if (checksum_field1 == crc32) {
787-
return(true);
788-
} else {
789-
const uint32_t crc32_legacy = buf_calc_page_crc32(read_buf, true);
790-
791-
if (checksum_field1 == crc32_legacy) {
792-
return(true);
793-
}
794-
}
795-
796-
return(false);
783+
return checksum_field1 == crc32
784+
#ifdef INNODB_BUG_ENDIAN_CRC32
785+
|| checksum_field1 == buf_calc_page_crc32(read_buf, true)
786+
#endif
787+
;
797788
}
798789

799790
/** Checks if the page is in innodb checksum format.
@@ -922,6 +913,29 @@ buf_page_is_checksum_valid_none(
922913
&& checksum_field1 == BUF_NO_CHECKSUM_MAGIC);
923914
}
924915

916+
#ifdef INNODB_BUG_ENDIAN_CRC32
917+
/** Validate the CRC-32C checksum of a page.
918+
@param[in] page buffer page (srv_page_size bytes)
919+
@param[in] checksum CRC-32C checksum stored on page
920+
@return computed checksum */
921+
static uint32_t buf_page_check_crc32(const byte* page, uint32_t checksum)
922+
{
923+
uint32_t crc32 = buf_calc_page_crc32(page);
924+
925+
if (checksum != crc32) {
926+
crc32 = buf_calc_page_crc32(page, true);
927+
}
928+
929+
return crc32;
930+
}
931+
#else /* INNODB_BUG_ENDIAN_CRC32 */
932+
/** Validate the CRC-32C checksum of a page.
933+
@param[in] page buffer page (srv_page_size bytes)
934+
@param[in] checksum CRC-32C checksum stored on page
935+
@return computed checksum */
936+
# define buf_page_check_crc32(page, checksum) buf_calc_page_crc32(page)
937+
#endif /* INNODB_BUG_ENDIAN_CRC32 */
938+
925939
/** Check if a page is corrupt.
926940
@param[in] check_lsn whether the LSN should be checked
927941
@param[in] read_buf database page
@@ -1037,26 +1051,20 @@ buf_page_is_corrupted(
10371051
&& *reinterpret_cast<const ib_uint64_t*>(
10381052
read_buf + FIL_PAGE_LSN) == 0) {
10391053

1040-
ulint i;
1041-
10421054
/* make sure that the page is really empty */
1043-
for (ulint i = 0; i < UNIV_PAGE_SIZE; i++) {
1055+
for (ulint i = 0; i < page_size.logical(); i++) {
10441056
if (read_buf[i] != 0) {
10451057
return(true);
10461058
}
10471059
}
10481060
#ifdef UNIV_INNOCHECKSUM
1049-
if (i >= page_size.logical()) {
1050-
if (log_file) {
1051-
fprintf(log_file, "Page::%llu"
1052-
" is empty and uncorrupted\n",
1053-
cur_page_num);
1054-
}
1055-
return(false);
1061+
if (log_file) {
1062+
fprintf(log_file, "Page::%llu"
1063+
" is empty and uncorrupted\n",
1064+
cur_page_num);
10561065
}
1057-
#else
1058-
return(i < page_size.logical());
10591066
#endif /* UNIV_INNOCHECKSUM */
1067+
return(false);
10601068
}
10611069

10621070
const srv_checksum_algorithm_t curr_algo =
@@ -1065,10 +1073,7 @@ buf_page_is_corrupted(
10651073
switch (curr_algo) {
10661074
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
10671075
return !buf_page_is_checksum_valid_crc32(
1068-
read_buf, checksum_field1, checksum_field2, false)
1069-
&& !buf_page_is_checksum_valid_crc32(
1070-
read_buf, checksum_field1, checksum_field2,
1071-
true);
1076+
read_buf, checksum_field1, checksum_field2);
10721077
case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
10731078
return !buf_page_is_checksum_valid_innodb(
10741079
read_buf, checksum_field1, checksum_field2);
@@ -1111,19 +1116,10 @@ buf_page_is_corrupted(
11111116

11121117
if (srv_checksum_algorithm
11131118
== SRV_CHECKSUM_ALGORITHM_CRC32) {
1114-
1115-
crc32 = buf_calc_page_crc32(
1116-
read_buf, legacy_big_endian_checksum);
1119+
crc32 = buf_page_check_crc32(read_buf,
1120+
checksum_field2);
11171121
crc32_inited = true;
11181122

1119-
if (!legacy_big_endian_checksum
1120-
&& checksum_field2 != crc32) {
1121-
crc32 = buf_calc_page_crc32(read_buf,
1122-
true);
1123-
legacy_big_endian_checksum =
1124-
checksum_field2 == crc32;
1125-
}
1126-
11271123
if (checksum_field2 != crc32
11281124
&& checksum_field2
11291125
!= buf_calc_page_old_checksum(read_buf)) {
@@ -1135,19 +1131,10 @@ buf_page_is_corrupted(
11351131

11361132
if (checksum_field2
11371133
!= buf_calc_page_old_checksum(read_buf)) {
1138-
crc32 = buf_calc_page_crc32(
1139-
read_buf,
1140-
legacy_big_endian_checksum);
1134+
crc32 = buf_page_check_crc32(
1135+
read_buf, checksum_field2);
11411136
crc32_inited = true;
11421137

1143-
if (!legacy_big_endian_checksum
1144-
&& checksum_field2 != crc32) {
1145-
crc32 = buf_calc_page_crc32(
1146-
read_buf, true);
1147-
legacy_big_endian_checksum =
1148-
checksum_field2 == crc32;
1149-
}
1150-
11511138
if (checksum_field2 != crc32) {
11521139
return true;
11531140
}
@@ -1161,18 +1148,9 @@ buf_page_is_corrupted(
11611148
== SRV_CHECKSUM_ALGORITHM_CRC32) {
11621149

11631150
if (!crc32_inited) {
1164-
crc32 = buf_calc_page_crc32(
1165-
read_buf,
1166-
legacy_big_endian_checksum);
1151+
crc32 = buf_page_check_crc32(
1152+
read_buf, checksum_field2);
11671153
crc32_inited = true;
1168-
1169-
if (!legacy_big_endian_checksum
1170-
&& checksum_field2 != crc32) {
1171-
crc32 = buf_calc_page_crc32(
1172-
read_buf, true);
1173-
legacy_big_endian_checksum =
1174-
checksum_field2 == crc32;
1175-
}
11761154
}
11771155

11781156
if (checksum_field1 != crc32
@@ -1188,18 +1166,9 @@ buf_page_is_corrupted(
11881166
!= buf_calc_page_new_checksum(read_buf)) {
11891167

11901168
if (!crc32_inited) {
1191-
crc32 = buf_calc_page_crc32(
1192-
read_buf,
1193-
legacy_big_endian_checksum);
1169+
crc32 = buf_page_check_crc32(
1170+
read_buf, checksum_field2);
11941171
crc32_inited = true;
1195-
1196-
if (!legacy_big_endian_checksum
1197-
&& checksum_field2 != crc32) {
1198-
crc32 = buf_calc_page_crc32(
1199-
read_buf, true);
1200-
legacy_big_endian_checksum =
1201-
checksum_field2 == crc32;
1202-
}
12031172
}
12041173

12051174
if (checksum_field1 != crc32) {
@@ -1255,10 +1224,12 @@ buf_page_print(const byte* read_buf, const page_size_t& page_size)
12551224
<< page_zip_calc_checksum(
12561225
read_buf, page_size.physical(),
12571226
SRV_CHECKSUM_ALGORITHM_CRC32)
1227+
#ifdef INNODB_BUG_ENDIAN_CRC32
12581228
<< "/"
12591229
<< page_zip_calc_checksum(
12601230
read_buf, page_size.physical(),
12611231
SRV_CHECKSUM_ALGORITHM_CRC32, true)
1232+
#endif
12621233
<< ", "
12631234
<< buf_checksum_algorithm_name(
12641235
SRV_CHECKSUM_ALGORITHM_INNODB)
@@ -1284,9 +1255,10 @@ buf_page_print(const byte* read_buf, const page_size_t& page_size)
12841255

12851256
} else {
12861257
const uint32_t crc32 = buf_calc_page_crc32(read_buf);
1287-
1258+
#ifdef INNODB_BUG_ENDIAN_CRC32
12881259
const uint32_t crc32_legacy = buf_calc_page_crc32(read_buf,
12891260
true);
1261+
#endif /* INNODB_BUG_ENDIAN_CRC32 */
12901262
ulint page_type = fil_page_get_type(read_buf);
12911263

12921264
ib::info() << "Uncompressed page, stored checksum in field1 "
@@ -1295,7 +1267,10 @@ buf_page_print(const byte* read_buf, const page_size_t& page_size)
12951267
<< ", calculated checksums for field1: "
12961268
<< buf_checksum_algorithm_name(
12971269
SRV_CHECKSUM_ALGORITHM_CRC32) << " "
1298-
<< crc32 << "/" << crc32_legacy
1270+
<< crc32
1271+
#ifdef INNODB_BUG_ENDIAN_CRC32
1272+
<< "/" << crc32_legacy
1273+
#endif
12991274
<< ", "
13001275
<< buf_checksum_algorithm_name(
13011276
SRV_CHECKSUM_ALGORITHM_INNODB) << " "
@@ -1312,7 +1287,10 @@ buf_page_print(const byte* read_buf, const page_size_t& page_size)
13121287
<< ", calculated checksums for field2: "
13131288
<< buf_checksum_algorithm_name(
13141289
SRV_CHECKSUM_ALGORITHM_CRC32) << " "
1315-
<< crc32 << "/" << crc32_legacy
1290+
<< crc32
1291+
#ifdef INNODB_BUG_ENDIAN_CRC32
1292+
<< "/" << crc32_legacy
1293+
#endif
13161294
<< ", "
13171295
<< buf_checksum_algorithm_name(
13181296
SRV_CHECKSUM_ALGORITHM_INNODB) << " "
@@ -3950,10 +3928,12 @@ buf_zip_decompress(
39503928
<< ", crc32: "
39513929
<< page_zip_calc_checksum(
39523930
frame, size, SRV_CHECKSUM_ALGORITHM_CRC32)
3931+
#ifdef INNODB_BUG_ENDIAN_CRC32
39533932
<< "/"
39543933
<< page_zip_calc_checksum(
39553934
frame, size, SRV_CHECKSUM_ALGORITHM_CRC32,
39563935
true)
3936+
#endif
39573937
<< " innodb: "
39583938
<< page_zip_calc_checksum(
39593939
frame, size, SRV_CHECKSUM_ALGORITHM_INNODB)

storage/innobase/buf/buf0checksum.cc

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,44 +39,56 @@ ha_innodb.cc:12251: error: cannot convert 'srv_checksum_algorithm_t*' to
3939
'long unsigned int*' in initialization */
4040
ulong srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_INNODB;
4141

42-
/** set if we have found pages matching legacy big endian checksum */
43-
bool legacy_big_endian_checksum = false;
44-
/** Calculates the CRC32 checksum of a page. The value is stored to the page
42+
#ifdef INNODB_BUG_ENDIAN_CRC32
43+
/** Calculate the CRC32 checksum of a page. The value is stored to the page
4544
when it is written to a file and also checked for a match when reading from
46-
the file. When reading we allow both normal CRC32 and CRC-legacy-big-endian
47-
variants. Note that we must be careful to calculate the same value on 32-bit
48-
and 64-bit architectures.
49-
@param[in] page buffer page (UNIV_PAGE_SIZE bytes)
50-
@param[in] use_legacy_big_endian if true then use big endian
51-
byteorder when converting byte strings to integers
52-
@return checksum */
53-
uint32_t
54-
buf_calc_page_crc32(
55-
const byte* page,
56-
bool use_legacy_big_endian /* = false */)
45+
the file. Note that we must be careful to calculate the same value on all
46+
architectures.
47+
@param[in] page buffer page (srv_page_size bytes)
48+
@param[in] bug_endian whether to use big endian byteorder
49+
when converting byte strings to integers, for bug-compatibility with
50+
big-endian architecture running MySQL 5.6, MariaDB 10.0 or MariaDB 10.1
51+
@return CRC-32C */
52+
uint32_t buf_calc_page_crc32(const byte* page, bool bug_endian)
5753
{
58-
/* Since the field FIL_PAGE_FILE_FLUSH_LSN, and in versions <= 4.1.x
59-
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, are written outside the buffer pool
60-
to the first pages of data files, we have to skip them in the page
61-
checksum calculation.
62-
We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the
63-
checksum is stored, and also the last 8 bytes of page because
64-
there we store the old formula checksum. */
65-
66-
ut_crc32_func_t crc32_func = use_legacy_big_endian
67-
? ut_crc32_legacy_big_endian
68-
: ut_crc32;
69-
70-
const uint32_t c1 = crc32_func(
71-
page + FIL_PAGE_OFFSET,
72-
FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION - FIL_PAGE_OFFSET);
73-
74-
const uint32_t c2 = crc32_func(
75-
page + FIL_PAGE_DATA,
76-
UNIV_PAGE_SIZE - FIL_PAGE_DATA - FIL_PAGE_END_LSN_OLD_CHKSUM);
77-
78-
return(c1 ^ c2);
54+
return bug_endian
55+
? ut_crc32_legacy_big_endian(
56+
page + FIL_PAGE_OFFSET,
57+
FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
58+
- FIL_PAGE_OFFSET)
59+
^ ut_crc32_legacy_big_endian(page + FIL_PAGE_DATA,
60+
srv_page_size
61+
- (FIL_PAGE_DATA
62+
+ FIL_PAGE_END_LSN_OLD_CHKSUM))
63+
: ut_crc32(page + FIL_PAGE_OFFSET,
64+
FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
65+
- FIL_PAGE_OFFSET)
66+
^ ut_crc32(page + FIL_PAGE_DATA,
67+
srv_page_size
68+
- (FIL_PAGE_DATA + FIL_PAGE_END_LSN_OLD_CHKSUM));
69+
}
70+
#else
71+
/** Calculate the CRC32 checksum of a page. The value is stored to the page
72+
when it is written to a file and also checked for a match when reading from
73+
the file. Note that we must be careful to calculate the same value on all
74+
architectures.
75+
@param[in] page buffer page (srv_page_size bytes)
76+
@return CRC-32C */
77+
uint32_t buf_calc_page_crc32(const byte* page)
78+
{
79+
/* Note: innodb_checksum_algorithm=crc32 could and should have
80+
included the entire page in the checksum, and CRC-32 values
81+
should be combined with the CRC-32 function, not with
82+
exclusive OR. We stick to the current algorithm in order to
83+
remain compatible with old data files. */
84+
return ut_crc32(page + FIL_PAGE_OFFSET,
85+
FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION
86+
- FIL_PAGE_OFFSET)
87+
^ ut_crc32(page + FIL_PAGE_DATA,
88+
srv_page_size
89+
- (FIL_PAGE_DATA + FIL_PAGE_END_LSN_OLD_CHKSUM));
7990
}
91+
#endif
8092

8193
/** Calculate a checksum which is stored to the page when it is written
8294
to a file. Note that we must be careful to calculate the same value on

storage/innobase/fil/fil0crypt.cc

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,10 +2636,8 @@ fil_space_verify_crypt_checksum(
26362636
srv_checksum_algorithm);
26372637
switch (algorithm) {
26382638
case SRV_CHECKSUM_ALGORITHM_STRICT_CRC32:
2639-
/* We never supported upgrade from the "legacy crc32"
2640-
on big endian systems from MariaDB 10.1 to later. */
26412639
valid = buf_page_is_checksum_valid_crc32(
2642-
page, checksum1, checksum2, false);
2640+
page, checksum1, checksum2);
26432641
break;
26442642
case SRV_CHECKSUM_ALGORITHM_STRICT_INNODB:
26452643
valid = buf_page_is_checksum_valid_innodb(
@@ -2649,13 +2647,11 @@ fil_space_verify_crypt_checksum(
26492647
case SRV_CHECKSUM_ALGORITHM_CRC32:
26502648
case SRV_CHECKSUM_ALGORITHM_INNODB:
26512649
case SRV_CHECKSUM_ALGORITHM_NONE:
2652-
/* We never supported upgrade from the "legacy crc32"
2653-
on big endian systems from MariaDB 10.1 to later.
2654-
We also never supported
2650+
/* never supported
26552651
innodb_checksum_algorithm=none or strict_none
26562652
for encrypted pages. */
26572653
valid = buf_page_is_checksum_valid_crc32(
2658-
page, checksum1, checksum2, false)
2654+
page, checksum1, checksum2)
26592655
|| buf_page_is_checksum_valid_innodb(
26602656
page, checksum1, checksum2);
26612657
break;
@@ -2684,8 +2680,11 @@ fil_space_verify_crypt_checksum(
26842680
ib::info()
26852681
<< "If unencrypted: stored checksum [" << checksum1
26862682
<< ":" << checksum2 << "] calculated crc32 ["
2687-
<< buf_calc_page_crc32(page, false) << ":"
2688-
<< buf_calc_page_crc32(page, true) << "] innodb ["
2683+
<< buf_calc_page_crc32(page)
2684+
# ifdef INNODB_BUG_ENDIAN_CRC32
2685+
<< ":" << buf_calc_page_crc32(page, true)
2686+
# endif /* INNODB_BUG_ENDIAN_CRC32 */
2687+
<< "] innodb ["
26892688
<< buf_calc_page_old_checksum(page) << ":"
26902689
<< buf_calc_page_new_checksum(page) << "] LSN "
26912690
<< mach_read_from_4(page + FIL_PAGE_LSN);

0 commit comments

Comments
 (0)