ikudrin created this revision. ikudrin added reviewers: mclow.lists, howard.hinnant, EricWF. ikudrin added a subscriber: cfe-commits.
Throwing an exception for the first time may lead to calling calloc to allocate memory for __cxa_eh_globals. If the memory pool at that moment is exhausted, it results in abnormal termination of the program. This patch addresses the issue by using fallback_malloc in that case. http://reviews.llvm.org/D17815 Files: libcxxabi/trunk/src/cxa_exception_storage.cpp libcxxabi/trunk/test/test_exception_storage_nodynmem.pass.cpp Index: libcxxabi/trunk/test/test_exception_storage_nodynmem.pass.cpp =================================================================== --- /dev/null +++ libcxxabi/trunk/test/test_exception_storage_nodynmem.pass.cpp @@ -0,0 +1,21 @@ +//===--------------- test_exception_storage_nodynmem.cpp ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cstdlib> + +// Override calloc to simulate exhaustion of dynamic memory +void *calloc(size_t, size_t) { return 0; } + +int main(int argc, char *argv[]) { + try { + throw 42; + } catch (...) { + } + return 0; +} Index: libcxxabi/trunk/src/cxa_exception_storage.cpp =================================================================== --- libcxxabi/trunk/src/cxa_exception_storage.cpp +++ libcxxabi/trunk/src/cxa_exception_storage.cpp @@ -46,19 +46,38 @@ #include <pthread.h> #include <cstdlib> // for calloc, free +#include <cstring> // for memset #include "abort_message.h" // In general, we treat all pthread errors as fatal. // We cannot call std::terminate() because that will in turn // call __cxa_get_globals() and cause infinite recursion. namespace __cxxabiv1 { + +#include "fallback_malloc.ipp" + +// Allocate some memory from _somewhere_ +static void *do_calloc(size_t count, size_t size) { + void *ptr = std::calloc(count, size); + if (NULL == ptr) { // if calloc fails, fall back to emergency stash + ptr = fallback_malloc(size * count); + if (NULL != ptr) + std::memset(ptr, 0, size * count); + } + return ptr; +} + +static void do_free(void *ptr) { + is_fallback_ptr(ptr) ? fallback_free(ptr) : std::free(ptr); +} + namespace { pthread_key_t key_; pthread_once_t flag_ = PTHREAD_ONCE_INIT; void destruct_ (void *p) { - std::free ( p ); + do_free ( p ); if ( 0 != ::pthread_setspecific ( key_, NULL ) ) abort_message("cannot zero out thread value for __cxa_get_globals()"); } @@ -77,7 +96,7 @@ // If this is the first time we've been asked for these globals, create them if ( NULL == retVal ) { retVal = static_cast<__cxa_eh_globals*> - (std::calloc (1, sizeof (__cxa_eh_globals))); + (do_calloc (1, sizeof (__cxa_eh_globals))); if ( NULL == retVal ) abort_message("cannot allocate __cxa_eh_globals"); if ( 0 != pthread_setspecific ( key_, retVal ) )
Index: libcxxabi/trunk/test/test_exception_storage_nodynmem.pass.cpp =================================================================== --- /dev/null +++ libcxxabi/trunk/test/test_exception_storage_nodynmem.pass.cpp @@ -0,0 +1,21 @@ +//===--------------- test_exception_storage_nodynmem.cpp ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cstdlib> + +// Override calloc to simulate exhaustion of dynamic memory +void *calloc(size_t, size_t) { return 0; } + +int main(int argc, char *argv[]) { + try { + throw 42; + } catch (...) { + } + return 0; +} Index: libcxxabi/trunk/src/cxa_exception_storage.cpp =================================================================== --- libcxxabi/trunk/src/cxa_exception_storage.cpp +++ libcxxabi/trunk/src/cxa_exception_storage.cpp @@ -46,19 +46,38 @@ #include <pthread.h> #include <cstdlib> // for calloc, free +#include <cstring> // for memset #include "abort_message.h" // In general, we treat all pthread errors as fatal. // We cannot call std::terminate() because that will in turn // call __cxa_get_globals() and cause infinite recursion. namespace __cxxabiv1 { + +#include "fallback_malloc.ipp" + +// Allocate some memory from _somewhere_ +static void *do_calloc(size_t count, size_t size) { + void *ptr = std::calloc(count, size); + if (NULL == ptr) { // if calloc fails, fall back to emergency stash + ptr = fallback_malloc(size * count); + if (NULL != ptr) + std::memset(ptr, 0, size * count); + } + return ptr; +} + +static void do_free(void *ptr) { + is_fallback_ptr(ptr) ? fallback_free(ptr) : std::free(ptr); +} + namespace { pthread_key_t key_; pthread_once_t flag_ = PTHREAD_ONCE_INIT; void destruct_ (void *p) { - std::free ( p ); + do_free ( p ); if ( 0 != ::pthread_setspecific ( key_, NULL ) ) abort_message("cannot zero out thread value for __cxa_get_globals()"); } @@ -77,7 +96,7 @@ // If this is the first time we've been asked for these globals, create them if ( NULL == retVal ) { retVal = static_cast<__cxa_eh_globals*> - (std::calloc (1, sizeof (__cxa_eh_globals))); + (do_calloc (1, sizeof (__cxa_eh_globals))); if ( NULL == retVal ) abort_message("cannot allocate __cxa_eh_globals"); if ( 0 != pthread_setspecific ( key_, retVal ) )
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits