Since #15233 has been merged, the `lockdata` becomes a static local variable with a well-defined initialization order: https://github.com/bitcoin/bitcoin/blob/36c0abd8f61ba859d53b1e59014720282c97c143/src/sync.cpp#L93-L96

Its destructor `~LockData()` [is called at program exit](https://en.cppreference.com/w/cpp/utility/program/exit).

At the same time, "if a function-local ... static object was destroyed and then that function is called from the destructor..., the _behavior is undefined_."

It seems possible that at program exit the `lockdata` object is destroyed before a global `RecursiveMutex` destructor calls `DeleteLock()`, which in turn calls `GetLockData()` with UB.
{
        return strprintf(
            "%s %s:%s%s (in thread %s)",
            mutexName, sourceFile, sourceLine, (fTry ? " (TRY)" : ""), m_thread_name);
    }
    std::string Name() const
    {
        return mutexName;
    }
private:
    bool fTry;
    std::string mutexName;
    std::string sourceFile;
    const std::string& m_thread_name;
    int sourceLine;
};
typedef std::vector<std::pair<void*, CLockLocation> > LockStack;
typedef std::map<std::pair<void*, void*>, LockStack> LockOrders;
typedef std::set<std::pair<void*, void*> > InvLockOrders;
struct LockData {
    
    
    
    
    bool available;
    LockData() : available(true) {}