logging: Protect ShrinkDebugFile by m_cs
What changed, and why it matters
This commit fixes a potential crash or data corruption bug in Bitcoin Core's debug log handling. The ShrinkDebugFile() function, which trims the large debug.log file down to size, was not using the same lock (mutex) that protects normal logging. That meant logging could happen at the same time the file was being shrunk, possibly causing the program to behave incorrectly or crash. The fix wraps the shrink operation in the logging mutex and changes one internal warning call so it works while that lock is held.
Treat as a routine stability/defensive fix. No immediate emergency response is warranted; the issue is local to debug logging and does not affect consensus, wallet funds, or P2P protocol handling. Users and downstream packagers should include this patch in their next update cycle.
Security signals we found
Concurrency / locking fix around shared file resource
Potential use-after-close or file descriptor race if logging occurred during fclose/rewind/fseek sequence
Annotation change enforces lock discipline at compile time (Clang thread-safety analysis)
Avoids possible crash or log corruption from unsynchronized access to debug.log
Evidence from the diff
BCLog::Logger::ShrinkDebugFile() now acquires m_cs for the entire operation. Because LogWarning() itself requires !m_cs, the internal failure path now calls LogPrint_ directly with an explicit Entry so it can log while already holding m_cs. The header annotation is updated to EXCLUSIVE_LOCKS_REQUIRED(!m_cs), matching the new contract that callers must not hold the lock. This resolves a race between concurrent logging and debug-log rotation/shrinking.
Changed components
src/logging.cppsrc/logging.hBCLog::Logger::ShrinkDebugFile()debug.log file handlingInspect captured patch +11 / −2
diff --git a/src/logging.cpp b/src/logging.cpp
index 3373063c..8f523128 100644
--- a/src/logging.cpp
+++ b/src/logging.cpp
@@ -514,6 +514,8 @@ void BCLog::Logger::LogPrint_(util::log::Entry entry)
void BCLog::Logger::ShrinkDebugFile()
{
+ STDLOCK(m_cs);
+
// Amount of debug.log to save at end when shrinking (must fit in memory)
constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
@@ -535,7 +537,14 @@ void BCLog::Logger::ShrinkDebugFile()
// Restart the file with some of the end
std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
if (fseek(file, -((long)vch.size()), SEEK_END)) {
- LogWarning("Failed to shrink debug log file: fseek(...) failed");
+ // LogWarning, except with m_cs held
+ LogPrint_({
+ .category = BCLog::ALL,
+ .level = Level::Warning,
+ .should_ratelimit = true,
+ .source_loc = SourceLocation{__func__},
+ .message = "Failed to shrink debug log file: fseek(...) failed",
+ });
fclose(file);
return;
}
diff --git a/src/logging.h b/src/logging.h
index 67f5d6ef..4d406942 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -227,7 +227,7 @@ namespace BCLog {
*/
void DisableLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
- void ShrinkDebugFile();
+ void ShrinkDebugFile() EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
std::unordered_map<LogFlags, Level> CategoryLevels() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
{
Why this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.