From dcc09afa634b0f525d2e8e7d15ef36e5fdd5d270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 31 Jan 2018 13:13:29 +0200 Subject: [PATCH] Follow-up fix to MDEV-15132 Avoid accessing the TRX_SYS page trx_undo_mem_create_at_db_start(): Do not read TRX_UNDO_TRX_NO unless the field is known to be valid, that is, the transaction has been serialized and trx_purge_add_undo_to_history() has been invoked. Normally InnoDB pages would be zero-initialized on allocation (since MySQL 5.5 or so), but the undo log pages skip that mechanism. So, reused undo log pages can contain garbage. Undo log headers can start at any offset (there can be multiple undo log headers in the same undo log page). Therefore, because the TRX_UNDO_TRX_NO is never explicitly initialized on undo log header creation, its contents may be garbage. --- storage/innobase/trx/trx0undo.cc | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index 2e6078bd2f600..8465d044d0372 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -1113,12 +1113,7 @@ trx_undo_mem_create_at_db_start(trx_rseg_t* rseg, ulint id, ulint page_no, xid.null(); } - trx_id_t trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_NO); - if (trx_id > max_trx_id) { - max_trx_id = trx_id; - } - - trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_ID); + trx_id_t trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_ID); if (trx_id > max_trx_id) { max_trx_id = trx_id; } @@ -1139,6 +1134,15 @@ trx_undo_mem_create_at_db_start(trx_rseg_t* rseg, ulint id, ulint page_no, ut_ad(type == TRX_UNDO_INSERT); state = TRX_UNDO_TO_PURGE; } else { + if (state == TRX_UNDO_TO_PURGE + || state == TRX_UNDO_CACHED) { + trx_id_t id = mach_read_from_8(TRX_UNDO_TRX_NO + + undo_header); + if (id > max_trx_id) { + max_trx_id = id; + } + } + fil_addr_t last_addr = flst_get_last( TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + undo_page, &mtr);