https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118811
Bug ID: 118811 Summary: SIGABRT (or ASAN heap-use-after-free) in chrono::locate_zone--chrono::tzdb_list during exit handlers Product: gcc Version: 14.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: nicholas at nicholaswilliams dot net Target Milestone: --- Created attachment 60437 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=60437&action=edit 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. If it helps, I can say that our exact use case involves an internal logging library that uses a background std::thread to flush log events to their intended destination(s). This is done because our real-time application behavior is very performance sensitive and we don't want anything to be blocked by log flushes. The background thread is kept alive until the very latest moment possible to avoid losing log events. I thought it would be useful to have the details from a system on which the bug does not occur (Ubuntu 24.04 with GCC 13.3.0) to compare against the details from a system on which the bug does occur (RHEL 9 with GCC 14.2.1), so I have attached the .ii files, "-v" compile output, and runtime output from *both* systems. (Or at least, I'm going to try to. It seems to only allow me to attach a single file, so I'm going to try to attach additional files after creating this.) Apologies if this is too much or not any more helpful than had I just attached the files from the buggy system. I compiled on both systems with this command/these options: $ g++ -v -save-temps -std=c++20 -fsanitize=address -fsanitize-address-use-after-scope -Wall -Wextra -Werror -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG -o gcc_chrono_segfault gcc_chrono_segfault.cpp The "-D_GLIBCXX_ASSERTIONS -D_GLIBCXX_DEBUG" was added as instructed in the bug reporting instructions. Without those, the behavior is identical (still works with GCC 13, still fails with GCC 14). The use of Address Sanitizer here is because it provides substantial details about the source of the problem, but without it the application still fails with SIGABRT (134). I don't claim to be an expert on the C++ specification, but my understanding is that since it is not explicitly stated that time zones are unsafe to use during exit handlers, they should be safe to use during exit handlers. We certainly haven't had a problem using any other C++ features (I/O streams, threads, strings, even other chrono features). >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, the shared_ptr in chrono gets destructed before our logging thread shared_ptr. In my opinion, this data should either be stored in some more-durable (longer-living) way, or it should be re-loaded if the shared_ptr is found to be invalid. Perhaps, even, there should be some kind of fallback plan. I'm not certain on the best course of action. In our case, we were previously using gmtime_r, std::put_time, etc. to format our timestamp, so our workaround for this problem is to revert the change we made that took advantage of this new C++20 Chrono feature. I've been unable to devise any other kind of workaround that still allows us to use std::chrono::time_zone. Happy to provide any more information or try any other things as needed. Just ask.