Skip to content

LoggerSystem::Shutdown clears m_loggers without holding m_loggersMutex #139

@JustinMDotNet

Description

@JustinMDotNet

Summary

LoggerSystem::Log correctly takes m_loggersMutex around the lookup/emplace into m_loggers. LoggerSystem::Shutdown does not take the mutex when it iterates and clears the same std::unordered_map. Concurrent calls to Log from any thread that is still alive during shutdown race against the iteration and clear.

Where

  • src/dll/Systems/LoggerSystem.cpp:22-35Shutdown iterates and calls m_loggers.clear() with no lock.
  • src/dll/Systems/LoggerSystem.hpp:40-76Log<T> takes std::scoped_lock _(m_loggersMutex); around the map access.

The reverse-order shutdown in App::Shutdown (src/dll/App.cpp:170-175) calls LoggerSystem::Shutdown last, so plugin worker threads and in-flight hook callbacks can still be running at that point — their DLLs are still mapped, and the RED4ext-installed detours have not been removed yet (those are detached later, in App::Destruct).

Why it matters

Data race on m_loggers:

  • Log may emplace into the map while Shutdown is mid-iteration → iterator invalidation, crash or torn read.
  • Log may read from the map while Shutdown has just called clear() → use-after-clear.

The window is narrow but not theoretical. The shutdown ordering bug tracked in #ISSUE-H6 makes this race more likely to fire in practice.

Suggested fix

Take the lock at the top of Shutdown and hold it for the whole flush + clear:

void LoggerSystem::Shutdown()
{
    std::scoped_lock _(m_loggersMutex);

    auto count = m_loggers.size();
    spdlog::trace("Flusing {} logger(s)...", count);

    for (auto& [plugin, logger] : m_loggers)
    {
        logger->flush();
    }

    m_loggers.clear();
    spdlog::trace("{} logger(s) flushed", count);
}

Severity

High. The fix is one line; the cost of leaving it is a rare-but-real crash during shutdown that will be hell to debug from a user-submitted log.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions