From f33e9904c1ffcab0c8b3c237a9bfb121c8e2e194 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= <marko.makela@mariadb.com>
Date: Fri, 3 Mar 2023 10:10:17 +0200
Subject: [PATCH] WIP test case

---
 .../suite/innodb/r/log_corruption.result      |  26 +--
 mysql-test/suite/innodb/t/log_corruption.test | 193 +++++++++++++++---
 storage/innobase/dict/dict0crea.cc            |  23 ++-
 storage/innobase/trx/trx0sys.cc               |   2 +-
 4 files changed, 197 insertions(+), 47 deletions(-)

diff --git a/mysql-test/suite/innodb/r/log_corruption.result b/mysql-test/suite/innodb/r/log_corruption.result
index df58a90dcec..b143bdeff8b 100644
--- a/mysql-test/suite/innodb/r/log_corruption.result
+++ b/mysql-test/suite/innodb/r/log_corruption.result
@@ -20,7 +20,7 @@ AND support IN ('YES', 'DEFAULT', 'ENABLED');
 ENGINE	SUPPORT	COMMENT	TRANSACTIONS	XA	SAVEPOINTS
 FOUND 1 /InnoDB: Upgrade after a crash is not supported. This redo log was created before MariaDB 10\.2\.2, and we did not find a valid checkpoint/ in mysqld.1.err
 # empty redo log from before MariaDB 10.2.2
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -28,7 +28,7 @@ COUNT(*)
 1
 FOUND 1 /InnoDB: Upgrading redo log:/ in mysqld.1.err
 # Corrupted multi-file redo log from before MariaDB 10.2.2
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -36,7 +36,7 @@ COUNT(*)
 0
 FOUND 1 /InnoDB: Upgrade after a crash is not supported. This redo log was created before MariaDB 10\.2\.2, and it appears corrupted/ in mysqld.1.err
 # Empty multi-file redo log (wrong offset) from before MariaDB 10.2.2
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -45,7 +45,7 @@ COUNT(*)
 FOUND 3 /Upgrade after a crash is not supported. This redo log was created before MariaDB 10\.2\.2, and we did not find a valid checkpoint\./ in mysqld.1.err
 # Multi-file redo log with size mismatch from after MariaDB 10.2.2
 # Corrupted multi-file redo log from after MariaDB 10.2.2
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -53,7 +53,7 @@ COUNT(*)
 0
 FOUND 3 /Upgrade after a crash is not supported. This redo log was created before MariaDB 10\.2\.2, and we did not find a valid checkpoint\./ in mysqld.1.err
 FOUND 1 /InnoDB: No valid checkpoint was found; the log was created with BogoDB 1\.2\.3\.4\./ in mysqld.1.err
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -62,7 +62,7 @@ COUNT(*)
 FOUND 1 /InnoDB: Log file .*ib_logfile1 is of different size 2097152 bytes than other log files 1048576 bytes!/ in mysqld.1.err
 FOUND 2 /InnoDB: No valid checkpoint was found; the log was created with BogoDB 1\.2\.3\.4\./ in mysqld.1.err
 # Empty multi-file redo log from after MariaDB 10.2.2
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -92,7 +92,7 @@ AND support IN ('YES', 'DEFAULT', 'ENABLED');
 ENGINE	SUPPORT	COMMENT	TRANSACTIONS	XA	SAVEPOINTS
 FOUND 1 /InnoDB: No valid checkpoint was found; the log was created with malicious intentions, or perhaps\./ in mysqld.1.err
 # valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2, invalid block checksum
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3
 SELECT * FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -100,7 +100,7 @@ ENGINE	SUPPORT	COMMENT	TRANSACTIONS	XA	SAVEPOINTS
 FOUND 2 /InnoDB: Invalid log header checksum/ in mysqld.1.err
 FOUND 1 /InnoDB: Upgrade after a crash is not supported\. The redo log was created with malicious intentions, or perhaps, and it appears corrupted\./ in mysqld.1.err
 # same, but with current-version header
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3
 SELECT * FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -115,7 +115,7 @@ ENGINE	SUPPORT	COMMENT	TRANSACTIONS	XA	SAVEPOINTS
 InnoDB	YES	Supports transactions, row-level locking, foreign keys and encryption for tables	YES	YES	YES
 FOUND 1 /\[Note\] InnoDB: log sequence number 0.*; transaction id 0/ in mysqld.1.err
 # valid header, valid checkpoint 1, all-zero (invalid) checkpoint 2, invalid block number
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3
 SELECT * FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -167,7 +167,7 @@ AND support IN ('YES', 'DEFAULT', 'ENABLED');
 ENGINE	SUPPORT	COMMENT	TRANSACTIONS	XA	SAVEPOINTS
 FOUND 3 /\[ERROR\] InnoDB: Upgrade after a crash is not supported\. The redo log was created with MariaDB 10\.3\.1\./ in mysqld.1.err
 # Empty 10.3 redo log
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -175,7 +175,7 @@ COUNT(*)
 1
 FOUND 1 /InnoDB: log sequence number 1213964\b.*; transaction id 0/ in mysqld.1.err
 # Empty 10.2 redo log
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -183,7 +183,7 @@ COUNT(*)
 1
 FOUND 3 /InnoDB: Upgrading redo log:/ in mysqld.1.err
 # Empty 10.5 redo log
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5 --innodb-log-file-size=4m
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3 --innodb-log-file-size=4m
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
@@ -191,7 +191,7 @@ COUNT(*)
 1
 FOUND 4 /InnoDB: Upgrading redo log:/ in mysqld.1.err
 # Minimal MariaDB 10.1.21 encrypted redo log
-# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=5
+# restart: --innodb-data-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-log-group-home-dir=MYSQLTEST_VARDIR/tmp/log_corruption --innodb-force-recovery=3
 SELECT * FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
 AND support IN ('YES', 'DEFAULT', 'ENABLED');
diff --git a/mysql-test/suite/innodb/t/log_corruption.test b/mysql-test/suite/innodb/t/log_corruption.test
index 6f7080f5b50..c4a2336a27c 100644
--- a/mysql-test/suite/innodb/t/log_corruption.test
+++ b/mysql-test/suite/innodb/t/log_corruption.test
@@ -47,28 +47,114 @@ binmode OUT;
 # bytes $page_size-8..$page_size-1 (checksum, LSB of FIL_PAGE_LSN)
 my $polynomial = 0x82f63b78; # CRC-32C
 
-# Tablespace header page with valid FSP_SIZE=768 pages.
-# Also, write a dummy FSEG_MAGIC_N at offset 60 to keep fseg_inode_try_get()
-# happy when fseg_n_reserved_pages() is following an invalid pointer
-# from the all-zero change buffer header page (page 3).
-## FIL_PAGE_OFFSET
-my $head = pack("Nx[18]", 0);
-## FSP_PAGE_SIZE, # FSEG_MAGIC_N
-my $body = pack("x[8]Nx[10]Nx[16312]", 768, 97937874);
+# Tablespace header page with valid FSP_SIZE=768 pages and allocation metadata.
+## FIL_PAGE_OFFSET, FIL_PAGE_TYPE
+my $head = pack("Nx[16]n", 0, 8);
+## FSP_PAGE_SIZE and some allocation metadata
+my $body = pack("x[8]N", 768) .
+  pack("Nx[4]Nx[4]NnNn", 320, 0x3a, ~0, 0, ~0, 0) . # 0x004e
+  pack("NxxNxxN", 1, 0x13e, 0x13e) . # 0x005e
+  pack("NxxNxxN", 2, 0x9e, 0x116) . # 0x006e
+  pack("x[4]NNN", 0xa4, 1, 2) . # 0x007e
+  pack("nNnNN", 0x26, 2, 0x26, 1, 0xf3) . # 0x008e
+  pack("nNnNN", 0x26, 0xf3, 0x26, 0, 0) . # 0x009e
+  pack("Nx[4]NN", ~0, 0x116, 3) . # 0x00ae
+  chr(170) x 16 . pack("x[6]nNx[6]", 15, ~0) . # 0x00d0
+  pack("nN", 0xee, 4) . chr(170) x 16 . pack("x[4]Nxx", 15) . # 0x00f0
+  pack("NNx[4]n", 0xc6, ~0, 4) . chr(170) x 16 . pack("x[12]nN", 0x9e, ~0) .
+  pack("x[4]n", 3) . chr(170) x 16 . # 0x0136
+  pack("x[8]NnNnN", ~0, 0, ~0, 0, 2) . chr(170) x 14 . pack("n", 0xFAFF) .
+  pack("x[16026]");
 my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
 print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
-# Dummy pages 1..6.
+# Dummy change buffer bitmap page (page 1).
 $body = pack("x[16338]");
-for (my($page) = 1; $page < 7; $page++)
-{
-    ## FIL_PAGE_OFFSET
-    $head = pack("Nx[18]", $page);
-    $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
-    print OUT pack("N",$ck).$head.pack("x[16350]Nx[4]",$ck);
-}
+## FIL_PAGE_OFFSET, FIL_PAGE_PREV, FIL_PAGE_NEXT
+$head = pack("NNNx[10]", 1, ~0, ~0);
+$ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
+# Inode page (page 2) taken from a MySQL 5.7 data file.
+## FIL_PAGE_OFFSET, FIL_PAGE_TYPE
+$head = pack("Nx[16]n", 2, 3);
+$body = pack("NnNnx[4]Nx[4]", ~0, 0, ~0, 0, 1) .
+  pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NNN", 0x5d669d2, 3, 4) . chr(255) x 120 .
+  pack("x[4]Nx[4]", 2) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 5) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 3) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 6) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 4) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 7) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 5) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 8) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 6) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128 .
+  pack("x[4]Nx[4]", 7) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 9) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 8) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128 .
+  pack("x[4]Nx[4]", 9) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 10) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 10) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128 .
+  pack("x[4]Nx[4]", 11) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 11) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 12) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128 . #0x8932
+  pack("x[4]Nx[4]", 13) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 12) . chr(255) x 124 . #0x89f2
+  pack("x[4]Nx[4]", 14) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128 . #0x8ab2
+  pack("x[4]Nx[4]", 15) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 2 .
+  pack("NNnNn", 2, 0, 0xc6, 0, 0xee) .
+  pack("N", 0x5d669d2); # 0x8af2
+for (my $i= 0x0d; $i <= 0x2c; $i++) {$body .= pack("N", $i)} # 0x8b72
+for (my $i= 16; $i < 35; $i++)
+{$body .=
+  pack("x[4]Nx[4]", $i) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x1d + $i) . chr(255) x 124
+} # 0x99b2
+# skip the doublewrite buffer pages (64..191)
+for (my $i= 35; $i < 86; $i++)
+{$body .=
+  pack("x[4]Nx[4]", $i) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x9d + $i) . chr(255) x 124
+} # 0xbff2
+$body .= pack("x[6]");
+$ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
+# Dummy change buffer header page (page 3).
+## FIL_PAGE_OFFSET, FIL_PAGE_PREV, FIL_PAGE_NEXT, FIL_PAGE_TYPE
+my $head = pack("NNNx[8]n", 3, 0xffffffff, 0xffffffff, 6);
+my $body = pack("x[62]nnx[16272]", 2, 50);
+my $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
+
+# Change buffer root page (page 4)
+## FIL_PAGE_OFFSET, FIL_PAGE_PREV, FIL_PAGE_NEXT, FIL_PAGE_TYPE
+$head = pack("NNNx[8]n", 4, ~0, ~0, 17855);
+## PAGE_INDEX_ID
+$body = pack("x[28]Nx[16306]", ~0);
+$ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
+# TRX_SYS page (page 5)
+## FIL_PAGE_OFFSET, FIL_PAGE_PREV, FIL_PAGE_NEXT, FIL_PAGE_TYPE
+$head = pack("NNNx[8]n", 5, ~0, ~0, 7);
+# TRX_SYS_TRX_ID_STORE, empty TRX_SYS_RSEGS, TRX_SYS_DOUBLEWRITE_MAGIC
+$body = pack("x[4]Nx[14]N", 123456, 6) . chr(255) x (127 * 8) .
+  pack("x[15114]NNNNNNx[158]", 536853855, 64, 128, 536853855, 64, 128);
+$ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
+# Rollback segment 0 (page 6)
+## FIL_PAGE_OFFSET, FIL_PAGE_PREV, FIL_PAGE_NEXT, FIL_PAGE_TYPE
+$head = pack("NNNx[8]n", 6, ~0, ~0, 6);
+## TRX_UNDO_STATE,TRX_UNDO_LAST_LOG
+$body = pack("Nx[30]", ~1) . (chr(255) x 4096) . pack("x[12208]");
+$ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
 # Dictionary header page (page 7).
-## FIL_PAGE_OFFSET
-$head = pack("Nx[18]", 7);
+## FIL_PAGE_OFFSET, FIL_PAGE_TYPE
+$head = pack("Nx[16]n", 7, 6);
 ## DICT_HDR_TABLES,DICT_HDR_INDEXES
 $body = pack("x[32]Nx[8]Nx[16290]", 8, 9);
 $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
@@ -77,8 +163,8 @@ print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
 # Empty SYS_TABLES page (page 8).
 ## FIL_PAGE_OFFSET, FIL_PAGE_PREV, FIL_PAGE_NEXT, FIL_PAGE_TYPE
 $head = pack("NNNx[8]n", 8, ~0, ~0, 17855);
-## PAGE_N_DIR_SLOTS, PAGE_HEAP_TOP, PAGE_INDEX_ID == DICT_TABLES_ID
-$body = pack("nnx[31]Cx[20]", 2, 124, 1);
+## PAGE_N_DIR_SLOTS, PAGE_HEAP_TOP, PAGE_INDEX_ID==DICT_TABLES_ID
+$body = pack("nnx[31]Cx[20]", 2, 125, 1);
 $body .= pack("nxnn", 0x801, 3, 116) . "infimum";
 $body .= pack("xnxnxx", 0x901, 0x803) . "supremum";
 $body .= pack("x[16248]nn", 116, 101);
@@ -95,8 +181,51 @@ $body .= pack("xnxnxx", 0x901, 0x803) . "supremum";
 $body .= pack("x[16248]nn", 116, 101);
 $ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
 print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
-
-print OUT chr(0) x (759 * 16384);
+print OUT chr(0) x (233 * 16384);
+# Page 243 (allocation metadata for trx_sys_create_rsegs())
+$head = pack("NNNx[8]n", 243, ~0, ~0, 3);
+$body = pack("NnNn", ~0, 0, ~0, 0); # 0x3cc032
+for (my $i= 0x56; $i < 0x71; $i++)
+{$body .=
+  pack("x[4]Nx[4]", $i) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x9e + $i) . chr(255) x 124
+} # 0x3cd472
+for (my $i= 0x71; $i < 0x79; $i+= 2)
+{$body .=
+  pack("x[4]Nx[4]", $i) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128 .
+  pack("x[4]Nx[4]", $i + 1) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x10f + ($i - 0x71) / 2) . chr(255) x 124
+} # 0x3cda72
+$body .=
+  pack("x[4]Nx[4]", 0x79) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x113) . chr(255) x 124;
+for (my $i= 0x7a; $i < 0x7e; $i+= 2)
+{$body .=
+  pack("x[4]Nx[4]", $i) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128 .
+  pack("x[4]Nx[4]", $i + 1) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x114 + ($i - 0x7a) / 2) . chr(255) x 124
+} # 0x3cde32
+$body .=
+  pack("x[4]Nx[4]", 0x7e) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x116) . chr(255) x 124 .
+  pack("x[4]Nx[4]", 0x7f) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("N", 0x5d669d2) . chr(255) x 128; # 0x3cdfb2
+for (my $i= 0x80; $i < 0x9f; $i++)
+{$body .=
+  pack("x[4]Nx[4]", $i) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x97 + $i) . chr(255) x 124
+} # 0x3cf6f2
+for (my $i= 0xa0; $i < 0xa4; $i++)
+{$body .=
+  pack("x[4]Nx[4]", $i) . pack("x[4]NnNn", ~0, 0, ~0, 0) x 3 .
+  pack("NN", 0x5d669d2, 0x96 + $i) . chr(255) x 124
+} # 0x3cf9f2
+$body .= pack("x[1542]");
+$ck = mycrc32($head, 0, $polynomial) ^ mycrc32($body, 0, $polynomial);
+print OUT pack("N",$ck).$head.pack("x[12]").$body.pack("Nx[4]",$ck);
+print OUT chr(0) x (525 * 16384);
 close OUT or die;
 
 # Create a redo log from before MariaDB 10.2.2/MySQL 5.7.9
@@ -163,7 +292,7 @@ die unless seek(OUT, 0x800, 0);
 print OUT pack("NnnNx[496]N", 0x80000944, 12, 12, 0, 0xb2a);
 close OUT or die;
 EOF
---let $restart_parameters= $dirs --innodb-force-recovery=5 --innodb-log-file-size=4m
+--let $restart_parameters= $dirs --innodb-force-recovery=3 --innodb-log-file-size=4m
 --source include/start_mysqld.inc
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
@@ -193,7 +322,7 @@ print OUT chr(0) x 2048;
 close OUT or die;
 EOF
 
---let $restart_parameters= $dirs --innodb-force-recovery=5 --innodb-log-file-size=4m
+--let $restart_parameters= $dirs --innodb-force-recovery=3 --innodb-log-file-size=4m
 --source include/start_mysqld.inc
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
@@ -212,7 +341,7 @@ print OUT chr(0) x 1536;
 close OUT or die;
 EOF
 
---let $restart_parameters= $dirs --innodb-force-recovery=5 --innodb-log-file-size=4m
+--let $restart_parameters= $dirs --innodb-force-recovery=3 --innodb-log-file-size=4m
 --source include/start_mysqld.inc
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
@@ -243,7 +372,7 @@ close OUT or die;
 EOF
 
 --echo # Corrupted multi-file redo log from after MariaDB 10.2.2
---let $restart_parameters= $dirs --innodb-force-recovery=5 --innodb-log-file-size=4m
+--let $restart_parameters= $dirs --innodb-force-recovery=3 --innodb-log-file-size=4m
 --source include/start_mysqld.inc
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
@@ -261,7 +390,7 @@ print OUT chr(0);
 close OUT or die;
 EOF
 
---let $restart_parameters= $dirs --innodb-force-recovery=5 --innodb-log-file-size=4m
+--let $restart_parameters= $dirs --innodb-force-recovery=3 --innodb-log-file-size=4m
 --source include/start_mysqld.inc
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
@@ -285,7 +414,7 @@ print OUT $_, pack("N", mycrc32($_, 0, $polynomial));
 close OUT or die;
 EOF
 
---let $restart_parameters= $dirs --innodb-force-recovery=5 --innodb-log-file-size=4m
+--let $restart_parameters= $dirs --innodb-force-recovery=3 --innodb-log-file-size=4m
 --source include/start_mysqld.inc
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
@@ -362,7 +491,7 @@ close OUT or die;
 EOF
 # Anything below innodb_force_recovery=6 must find a valid redo log.
 # Missing tablespace files are tolerated already with innodb_force_recovery=1.
---let $restart_parameters= $dirs --innodb-force-recovery=5
+--let $restart_parameters= $dirs --innodb-force-recovery=3
 --source include/start_mysqld.inc
 eval $check_no_innodb;
 --source include/shutdown_mysqld.inc
@@ -411,7 +540,7 @@ EOF
 --copy_file $bugdir/ib_logfile0 $bugdir/ib_logfile
 # Anything below innodb_force_recovery=6 must find an invalid redo log.
 # Missing tablespace files are tolerated already with innodb_force_recovery=1.
---let $restart_parameters= $dirs --innodb-force-recovery=5
+--let $restart_parameters= $dirs --innodb-force-recovery=3
 --source include/start_mysqld.inc
 eval $check_no_innodb;
 --source include/shutdown_mysqld.inc
@@ -541,7 +670,7 @@ print OUT pack("NnnNx[496]N", 0x80000944, 12, 12, 1, 0x46c8a2a2);
 close OUT or die;
 EOF
 
---let $restart_parameters= $dirs --innodb-force-recovery=5 --innodb-log-file-size=4m
+--let $restart_parameters= $dirs --innodb-force-recovery=3 --innodb-log-file-size=4m
 --source include/start_mysqld.inc
 SELECT COUNT(*) FROM INFORMATION_SCHEMA.ENGINES
 WHERE engine = 'innodb'
@@ -624,7 +753,7 @@ print OUT pack("H*", "80000c380016000c000000015cf22e8aff355642045605c22b97f7cba9
 close OUT or die;
 EOF
 
---let $restart_parameters= $dirs --innodb-force-recovery=5
+--let $restart_parameters= $dirs --innodb-force-recovery=3
 --source include/start_mysqld.inc
 if (!$no_cleanup) {
 eval $check_no_innodb;
diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc
index cce5f2f24d0..007736aefe1 100644
--- a/storage/innobase/dict/dict0crea.cc
+++ b/storage/innobase/dict/dict0crea.cc
@@ -1374,6 +1374,28 @@ dberr_t dict_sys_t::create_or_check_sys_tables()
   if (sys_tables_exist())
     return DB_SUCCESS;
 
+  /* Before attempting to create any system tables,
+  validate the hard-coded dictionary tables. */
+  mtr_t mtr;
+  dberr_t error;
+  dict_table_t *tables[4]= {sys_tables, sys_columns, sys_indexes, sys_fields};
+
+  for (size_t t= 0; t < 4; t++)
+    for (dict_index_t *index= UT_LIST_GET_FIRST(tables[t]->indexes); index;
+         index= UT_LIST_GET_NEXT(indexes, index))
+    {
+      mtr.start();
+      buf_block_t *root= btr_root_block_get(index, RW_S_LATCH, &mtr, &error);
+      if (root && !page_validate(root->page.frame, index))
+        error= DB_CORRUPTION;
+      mtr.commit();
+      if (!error)
+        continue;
+      sql_print_error("InnoDB: Dictionary table %s is corrupted",
+                      tables[t]->name.m_name);
+      return error;
+    }
+
   trx_t *trx= trx_create();
   trx_start_for_ddl(trx);
 
@@ -1398,7 +1420,6 @@ dberr_t dict_sys_t::create_or_check_sys_tables()
   /* System tables are always created inside the system tablespace. */
   const auto srv_file_per_table_backup= srv_file_per_table;
   srv_file_per_table= 0;
-  dberr_t error;
   span<const char> tablename;
 
   if (!sys_foreign)
diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc
index 374a9d724bc..54256fc0262 100644
--- a/storage/innobase/trx/trx0sys.cc
+++ b/storage/innobase/trx/trx0sys.cc
@@ -283,7 +283,7 @@ bool trx_sys_create_rsegs()
 			: TRX_SYS_SPACE;
 
 		if (!trx_rseg_create(space)) {
-			ib::error() << "Unable to allocate the"
+			ib::fatal() << "Unable to allocate the"
 				" requested innodb_undo_logs";
 			return(false);
 		}
-- 
2.39.2

