From eb0f3282ae8d1f3762c86d682dba99c892498049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 18 Jun 2026 09:20:45 +0300 Subject: [PATCH] MDEV-40063 Corruption due to race in SET GLOBAL innodb_log_archive=ON There was a race condition between log_t::write_checkpoint() and the execution of SET GLOBAL innodb_log_archive=ON (enabling log archiving). We had wrongly allowed the concurrent execution of log_t::set_archive() and log_t::write_checkpoint(). The result was that log_sys.next_checkpoint_no was corrupted. This could have broken crash recovery. log_t::write_checkpoint(): When we are releasing log_sys.latch while durably writing the checkpoint header block, assign log_sys.resize_log to log_sys.log to inform other threads that a checkpoint is in progress. Previously, we only did this when innodb_log_archive=ON holds. log_t::resize_start(): Relax a debug assertion for the logic change. Tested by: Matthias Leich --- storage/innobase/buf/buf0flu.cc | 25 +++++++++++++++---------- storage/innobase/log/log0log.cc | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index 5b869d6294bf7..756a8b85d4752 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -1993,8 +1993,6 @@ inline lsn_t log_t::write_checkpoint(lsn_t checkpoint, lsn_t end_lsn) noexcept (C[-1] && my_betoh64(C[-1]) < d)); ut_ad(!*C || offset + o == START_OFFSET - 8); *C= my_htobe64(d); - if (resize_log.m_file == OS_FILE_CLOSED) - resize_log= log; /* Block concurrent set_archive() */ goto write_checkpoint; } @@ -2029,6 +2027,8 @@ inline lsn_t log_t::write_checkpoint(lsn_t checkpoint, lsn_t end_lsn) noexcept { write_checkpoint: ut_ad(!is_mmap()); + if (resize_log.m_file == OS_FILE_CLOSED) + resize_log= log; /* Block concurrent set_archive() */ latch.wr_unlock(); log_write_and_flush_prepare(); resizing= resize_lsn.load(std::memory_order_relaxed); @@ -2059,7 +2059,18 @@ inline lsn_t log_t::write_checkpoint(lsn_t checkpoint, lsn_t end_lsn) noexcept last_checkpoint_lsn= checkpoint; this->end_lsn= end_lsn; if (!archive) + { archived_lsn= end_lsn; + checkpoint_completed: + if (resize_log.m_file == log.m_file) + { + /* We may have assigned resize_log= log to keep set_archived() out. */ +#ifdef HAVE_PMEM + ut_ad(!is_mmap() || (!resize_log.is_opened() && is_mmap_writeable())); +#endif + resize_log.m_file= OS_FILE_CLOSED; + } + } else if (archive_header_was_reset) { ut_ad(resize_log.m_file != log.m_file); @@ -2080,14 +2091,8 @@ inline lsn_t log_t::write_checkpoint(lsn_t checkpoint, lsn_t end_lsn) noexcept resize_log.close(); #endif } - else if (resize_log.m_file == log.m_file) - { - /* We may have assigned resize_log= log to keep set_archived() out. */ -#ifdef HAVE_PMEM - ut_ad(!is_mmap()); -#endif - resize_log.m_file= OS_FILE_CLOSED; - } + else + goto checkpoint_completed; DBUG_PRINT("ib_log", ("checkpoint ended at " LSN_PF ", flushed to " LSN_PF, checkpoint, get_flushed_lsn())); diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc index 302dde9ba9751..73d70455da2da 100644 --- a/storage/innobase/log/log0log.cc +++ b/storage/innobase/log/log0log.cc @@ -993,7 +993,7 @@ log_t::resize_start_status log_t::resize_start(os_offset_t size, void *thd) { lsn_t start_lsn; ut_ad(!resize_in_progress()); - ut_ad(!resize_log.is_opened()); + ut_ad(!resize_log.is_opened() || resize_log.m_file == log.m_file); ut_ad(!resize_buf); ut_ad(!resize_flush_buf); ut_ad(!resize_initiator);