Skip to content

Commit

Permalink
MDEV-18025: Improve test case and consistency checks
Browse files Browse the repository at this point in the history
Write a test case that computes valid crc32 checksums for
an encrypted page, but zeroes out the payload area, so
that the checksum after decryption fails.

xb_fil_cur_read(): Validate the page number before trying
any checksum calculation or decrypting or decompression.
Also, skip zero-filled pages. For page_compressed pages,
ensure that the FIL_PAGE_TYPE was changed. Also, reject
FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED if no decryption was attempted.
  • Loading branch information
dr-m committed Dec 19, 2018
1 parent 1b471fa commit dd72d7d
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 19 deletions.
38 changes: 30 additions & 8 deletions extra/mariabackup/fil_cur.cc
Expand Up @@ -353,6 +353,27 @@ xb_fil_cur_read(
&& page_no >= FSP_EXTENT_SIZE
&& page_no < FSP_EXTENT_SIZE * 3) {
/* We ignore the doublewrite buffer pages */
} else if (mach_read_from_4(page + FIL_PAGE_OFFSET) != page_no
&& space->id != TRX_SYS_SPACE) {
/* On pages that are not all zero, the
page number must match.
There may be a mismatch on tablespace ID,
because files may be renamed during backup.
We disable the page number check
on the system tablespace, because it may consist
of multiple files, and here we count the pages
from the start of each file.)
The first 38 and last 8 bytes are never encrypted. */
const ulint* p = reinterpret_cast<ulint*>(page);
const ulint* const end = reinterpret_cast<ulint*>(
page + cursor->page_size);
do {
if (*p++) {
goto corrupted;
}
} while (p != end);
} else if (mach_read_from_4(
page
+ FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION)
Expand All @@ -361,9 +382,6 @@ xb_fil_cur_read(
!= CRYPT_SCHEME_UNENCRYPTED
&& fil_space_verify_crypt_checksum(
page, cursor->zip_size)) {
ut_ad(mach_read_from_4(page + FIL_PAGE_SPACE_ID)
== space->id);

bool decrypted = false;

memcpy(tmp_page, page, cursor->page_size);
Expand All @@ -381,21 +399,25 @@ xb_fil_cur_read(
true, tmp_page, cursor->zip_size, space)) {
goto corrupted;
}

} else if (page_type == FIL_PAGE_PAGE_COMPRESSED) {
memcpy(tmp_page, page, cursor->page_size);
page_decomp:
ulint decomp = fil_page_decompress(tmp_frame, tmp_page);
page_type = mach_read_from_2(tmp_page + FIL_PAGE_TYPE);

if (!decomp
|| (decomp != srv_page_size && cursor->zip_size)
|| buf_page_is_corrupted(
true, tmp_page, cursor->zip_size, space)) {
|| page_type == FIL_PAGE_PAGE_COMPRESSED
|| page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
|| buf_page_is_corrupted(true, tmp_page,
cursor->zip_size,
space)) {
goto corrupted;
}

} else if (buf_page_is_corrupted(true, page, cursor->zip_size,
space)) {
} else if (page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
|| buf_page_is_corrupted(true, page,
cursor->zip_size, space)) {
corrupted:
retry_count--;

Expand Down
33 changes: 33 additions & 0 deletions mysql-test/suite/innodb/include/crc32.pl
@@ -0,0 +1,33 @@
# The following is Public Domain / Creative Commons CC0 from
# http://billauer.co.il/blog/2011/05/perl-crc32-crc-xs-module/

sub mycrc32 {
my ($input, $init_value, $polynomial) = @_;

$init_value = 0 unless (defined $init_value);
$polynomial = 0xedb88320 unless (defined $polynomial);

my @lookup_table;

for (my $i=0; $i<256; $i++) {
my $x = $i;
for (my $j=0; $j<8; $j++) {
if ($x & 1) {
$x = ($x >> 1) ^ $polynomial;
} else {
$x = $x >> 1;
}
}
push @lookup_table, $x;
}

my $crc = $init_value ^ 0xffffffff;

foreach my $x (unpack ('C*', $input)) {
$crc = (($crc >> 8) & 0xffffff) ^ $lookup_table[ ($crc ^ $x) & 0xff ];
}

$crc = $crc ^ 0xffffffff;

return $crc;
}
@@ -1,4 +1,4 @@
call mtr.add_suppression("\\[ERROR\\] InnoDB: The page .* in file .* cannot be decrypted.");
call mtr.add_suppression("\\[ERROR\\] InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=3\\] in file '.*test.t1\\.ibd' cannot be decrypted.");
CREATE TABLE t1(c VARCHAR(128)) ENGINE INNODB, encrypted=yes;
insert into t1 select repeat('a',100);
# Corrupt the table
Expand Down
43 changes: 33 additions & 10 deletions mysql-test/suite/mariabackup/encrypted_page_corruption.test
@@ -1,11 +1,22 @@
--source include/have_file_key_management.inc
--source include/innodb_page_size.inc

call mtr.add_suppression("\\[ERROR\\] InnoDB: The page .* in file .* cannot be decrypted.");
perl;
open(OUT, ">$ENV{MYSQLTEST_VARDIR}/log/check.txt") || die;
print OUT "--skip innodb_checksum_algorithm=crc32 needs little-endian\n"
unless unpack("L","macs")==unpack("N","scam");
close(OUT);
EOF

--source $MYSQLTEST_VARDIR/log/check.txt
--remove_file $MYSQLTEST_VARDIR/log/check.txt

call mtr.add_suppression("\\[ERROR\\] InnoDB: The page \\[page id: space=[1-9][0-9]*, page number=3\\] in file '.*test.t1\\.ibd' cannot be decrypted.");
CREATE TABLE t1(c VARCHAR(128)) ENGINE INNODB, encrypted=yes;
insert into t1 select repeat('a',100);

let $MYSQLD_DATADIR=`select @@datadir`;
let t1_IBD = $MYSQLD_DATADIR/test/t1.ibd;
let MYSQLD_DATADIR=`select @@datadir`;
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;

--source include/shutdown_mysqld.inc

Expand All @@ -15,17 +26,29 @@ perl;
use strict;
use warnings;
use Fcntl qw(:DEFAULT :seek);
do "$ENV{MTR_SUITE_DIR}/../innodb/include/crc32.pl";

my $page_size = $ENV{INNODB_PAGE_SIZE};

my $ibd_file = $ENV{'t1_IBD'};
sysopen IBD_FILE, "$ENV{MYSQLD_DATADIR}/test/t1.ibd", O_RDWR
|| die "Cannot open t1.ibd\n";
sysread(IBD_FILE, $_, 38) || die "Cannot read t1.ibd\n";
my $space = unpack("x[34]N", $_);
sysseek(IBD_FILE, $page_size * 3, SEEK_SET) || die "Cannot seek t1.ibd\n";

my $chunk;
my $len;
my $head = pack("Nx[18]", 3); # better to have a valid page number
my $body = chr(0) x ($page_size - 38 - 8);

sysopen IBD_FILE, $ibd_file, O_RDWR || die "Unable to open $ibd_file";
sysseek IBD_FILE, 16384 * 3, SEEK_CUR;
$chunk = '\xAA\xAA\xAA\xAA';
syswrite IBD_FILE, $chunk, 4;
# Calculate innodb_checksum_algorithm=crc32 for the unencrypted page.
# The following bytes are excluded:
# bytes 0..3 (the checksum is stored there)
# bytes 26..37 (encryption key version, post-encryption checksum, tablespace id)
# bytes $page_size-8..$page_size-1 (checksum, LSB of FIL_PAGE_LSN)
my $polynomial = 0x82f63b78; # CRC-32C
my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);

my $page= pack("N",$ck).$head.pack("NNN",1,$ck,$space).$body.pack("Nx[4]",$ck);
die unless syswrite(IBD_FILE, $page, $page_size) == $page_size;
close IBD_FILE;
EOF

Expand Down

0 comments on commit dd72d7d

Please sign in to comment.