https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118811
--- Comment #14 from Nicholas Williams <nicholas at nicholaswilliams dot net> --- (In reply to Jonathan Wakely from comment #11) > (In reply to Nicholas Williams from comment #0) > > Created attachment 60437 [details] > > Minimal reproduction application > > > > I have discovered a SIGABRT (or, when ASAN is enabled, a heap-use-after-free > > report) in std::chrono::locate_zone() when it calls > > std::chrono::tzdb_list::begin() as a result of C++20 Chrono library use > > during application shutdown (exit handlers). I'm not able to attach our > > proprietary code, so I have attached a fully functional minimal replication > > application that successfully demonstrates the problem. > > Doesn't your minimized program have a data race on the _running member? It probably does have a race condition on _running, yes. I was not being as careful about correctness writing this minimal replication program as I normally am. That said, it works okay in this case and isn't what's triggering the bug (our production code does not work as simplistically as this thread loop). > _pInstance is also accessed without synchronization but is only accessed > from the main thread so that doesn't have a data race. Yeah, it should be more like this. Again, sloppy on my part: instance() noexcept { if ( BackgroundThread::_pInstance ) { return BackgroundThread::_pInstance; } { std::unique_lock< std::mutex > lock( BackgroundThread::_instanceMutex ); if ( !BackgroundThread::_pInstance ) { BackgroundThread::_pInstance = std::make_shared< BackgroundThread >(); } } return BackgroundThread::_pInstance; } > > > From what I can tell reading through the ASAN report and the code in the > > chrono header, the problem appears to be related to the use of a chrono > > class's private static class data member that's a shared_ptr, without any > > checks about the validity of said shared_ptr. Since static class data > > members are destructed before static global variables, > > Are they? Where is that specified? I have definitely read that before, but cannot find it now that I need it. I could have misunderstood what I read at the time. > > > the shared_ptr in > > chrono gets destructed before our logging thread shared_ptr. > > Like all objects with static storage duration, it gets destroyed in reverse > order of construction. > > Because your logging thread shared_ptr is created early, before main, it is > constructed in an unspecified order compared to any other globals, including > the tzdb_list pointer. As Andrew said, there's a static initialization order > bug. > > This should be fixable by using __attribute__((init_priority(x))) inside > libstdc++. init_priority looks neat. That could indeed resolve this issue.