In __cxa_get_globals, the code does: if ((g = (__cxa_eh_globals *) std::malloc (sizeof (__cxa_eh_globals))) == 0 || __gthread_setspecific (globals_key, (void *) g) != 0) std::terminate (); but since __cxa_get_globals is called in __cxa_allocate_exception, the effect of this is that if the first exception thrown in a program is an out-of-memory exception, the program will instead call std::terminate, because it won't be able to allocate a new __cxa_eh_globals. std::terminate itself expects __cxa_get_globals to work, so it'll call itself recursively, leading to an abort() in __verbose_terminate_handler.
Confirmed.
Adding Rth in CC as the author of the fix of libstdc++/10606, which added the call of __cxa_get_globals from __cxa_allocate_exception
Created attachment 10391 [details] Trivial testcase Hi. I'm attaching a trivial testcase (*). In fact, I consider this bug pretty serious! Howard, any chance you can show your patch to Geoff or post it to the mailing list(s) for a review? (*) Of course, it reliably fails stand-alone, without the def-out bits, only on 32-bit machines equipped with less than ~ 1G.
Sorry. Actually the testcase is not correct stand-alone, fails at line 13. I'm currently checking whether is correct when the testsuite support (memory limits) is present.
... possibly using a "real" memory allocation, touching all the involved pages.
Created attachment 10392 [details] Puzzling testcase... Humm, strange. The new testcase *passes*... Still, the original analysis makes a lot of sense to me (like Howard's suggestion for a fix). But we badly need a testcase! Any idea?
Ah! Now I see, it's because of Benjamin and Ulrich recent changes, involving thread local storage. Therefore my amended testcase proposal should be fine on platforms not defining _GLIBCXX_HAVE_TLS. We should check that.
If my analysis is correct, this PR is still valid *only* for targets not defining _GLIBCXX_HAVE_TLS. Otherwise, fixed by: 2005-11-21 Benjamin Kosnik <bkoz@redhat.com> Ulrich Drepper <drepper@redhat.com> PR libstdc++/23591
The issue seems more tricky, even for TLS platforms, see: http://gcc.gnu.org/ml/gcc-patches/2006-10/msg00333.html
i know you are
I'm quite surprised to find that this problem still exists in the current trunk for platforms without TLS (i.e. many embedded platforms). Although I don't see a recursive call but a rather more direct chain of events: 1) We completely run out of heap memory calling operator new. To see this problem, less than the size of 'struct __cxa_eh_globals' must be available. 2) new throws std::bad_alloc, causing __cxa_allocate_exception to be called 3) __cxa_allocate_exception calls malloc() which still fails, but an emergency buffer is available, so it is able to continue. 4) But __cxa_allocate_exception then calls __cxa_get_globals() 5) If __cxa_get_globals() has not been called before, it will allocate the globals structure beginning with a call to malloc() 6) malloc() still fails, so std::terminate() is called. The effect is that if std::bad_alloc is the first C++ exception this thread has thrown, we can never catch it - instead the program is always abruptly terminated, by default with abort(). This seems to be a surprisingly serious issue given this is a 7 year old bug report. Jifl