From: Ivan Maidanski Date: Fri, 1 Jun 2018 08:29:41 +0000 (+0300) Subject: Never return null pointer by C++ operator new (gc_cpp) X-Git-Tag: v7.6.8~24 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ca769a4dfcde053f8b1772c0a78cef61b139c6fc;p=gc Never return null pointer by C++ operator new (gc_cpp) (a cherry-pick of commits cb1194d1, e965562c, 4399253c, 2ce94e20 from 'master') Now, in case of the allocation failure, new and new[] operators throw bad_alloc (or abort the application if an ancient compiler is used). * gc_cpp.cc: Include gc.h and "new" headers before include gc_cpp.h. * gc_cpp.cc (GC_DECL_DELETE_THROW): Move macro definition to gc_cpp.h. * gc_cpp.cc (GC_DECL_NEW_THROW): Define only if _MSC_VER. * gc_cpp.cc [!_MSC_VER] (operator new): Call GC_OP_NEW_OOM_CHECK(obj) after allocation. * gc_cpp.cc (GC_ALLOCATOR_THROW_OR_ABORT): New macro (the same definition as in gc_allocator.h). * gc_cpp.cc (GC_throw_bad_alloc): New API function definition. * include/gc.h (GC_abort_on_oom): Declare new API function. * include/gc_cpp.h (GC_OP_NEW_OOM_CHECK): New internal macro (throws bad_alloc or cals GC_abort_on_oom). * include/gc_cpp.h: Include "new" standard header only if GC_INCLUDE_NEW and !GC_NEW_ABORTS_ON_OOM and !_LIBCPP_NO_EXCEPTIONS. * gc_cpp.h [!GC_NEW_ABORTS_ON_OOM && !_LIBCPP_NO_EXCEPTIONS && !GC_INCLUDE_NEW] (GC_throw_bad_alloc): Declare API function. * include/gc_cpp.h (gc::new(size_t), gc::new(size_t,GCPlacement), new): Call GC_OP_NEW_OOM_CHECK() for the allocation result. * include/gc_cpp.h [GC_OPERATOR_NEW_ARRAY] (gc::new[](size_t), gc::new[](size_t,GCPlacement, new[]): Likewise. * misc.c (GC_abort_on_oom): Implement function. * tests/test.c [CPPCHECK] (main): Call UNTESTED(GC_abort_on_oom). --- diff --git a/gc_cpp.cc b/gc_cpp.cc index 103e66f8..7d4a2340 100644 --- a/gc_cpp.cc +++ b/gc_cpp.cc @@ -27,26 +27,42 @@ built-in "new" and "delete". # define GC_BUILD #endif -#include "gc_cpp.h" +#include "gc.h" -#if !defined(GC_NEW_DELETE_NEED_THROW) && defined(__GNUC__) \ - && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) -# define GC_NEW_DELETE_NEED_THROW -#endif +#include // for bad_alloc, precedes include of gc_cpp.h + +#include "gc_cpp.h" // for GC_OPERATOR_NEW_ARRAY, GC_DECL_DELETE_THROW -#ifdef GC_NEW_DELETE_NEED_THROW -# include /* for std::bad_alloc */ -# define GC_DECL_NEW_THROW throw(std::bad_alloc) -# define GC_DECL_DELETE_THROW throw() +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_ALLOCATOR_THROW_OR_ABORT() GC_abort_on_oom() #else -# define GC_DECL_NEW_THROW /* empty */ -# define GC_DECL_DELETE_THROW /* empty */ -#endif // !GC_NEW_DELETE_NEED_THROW +# define GC_ALLOCATOR_THROW_OR_ABORT() throw std::bad_alloc() +#endif + +GC_API void GC_CALL GC_throw_bad_alloc() { + GC_ALLOCATOR_THROW_OR_ABORT(); +} #ifndef _MSC_VER +# if !defined(GC_NEW_DELETE_THROW_NOT_NEEDED) \ + && !defined(GC_NEW_DELETE_NEED_THROW) \ + && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) \ + && (__cplusplus < 201103L || defined(__clang__)) +# define GC_NEW_DELETE_NEED_THROW +# endif + +# ifdef GC_NEW_DELETE_NEED_THROW +# define GC_DECL_NEW_THROW throw(std::bad_alloc) +# else +# define GC_DECL_NEW_THROW /* empty */ +# endif + void* operator new(size_t size) GC_DECL_NEW_THROW { - return GC_MALLOC_UNCOLLECTABLE(size); + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; } void operator delete(void* obj) GC_DECL_DELETE_THROW { @@ -55,7 +71,10 @@ built-in "new" and "delete". # if defined(GC_OPERATOR_NEW_ARRAY) && !defined(CPPCHECK) void* operator new[](size_t size) GC_DECL_NEW_THROW { - return GC_MALLOC_UNCOLLECTABLE(size); + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + if (0 == obj) + GC_ALLOCATOR_THROW_OR_ABORT(); + return obj; } void operator delete[](void* obj) GC_DECL_DELETE_THROW { diff --git a/include/gc.h b/include/gc.h index bf795335..da8202cd 100644 --- a/include/gc.h +++ b/include/gc.h @@ -1300,6 +1300,9 @@ typedef void (GC_CALLBACK * GC_abort_func)(const char * /* msg */); GC_API void GC_CALL GC_set_abort_func(GC_abort_func) GC_ATTR_NONNULL(1); GC_API GC_abort_func GC_CALL GC_get_abort_func(void); +/* A portable way to abort the application because of not enough memory.*/ +GC_API void GC_CALL GC_abort_on_oom(void); + /* The following is intended to be used by a higher level */ /* (e.g. Java-like) finalization facility. It is expected */ /* that finalization code will arrange for hidden pointers to */ diff --git a/include/gc_cpp.h b/include/gc_cpp.h index ed333af2..ede55cda 100644 --- a/include/gc_cpp.h +++ b/include/gc_cpp.h @@ -174,6 +174,32 @@ by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined. # define GC_PLACEMENT_DELETE #endif +#ifndef GC_DECL_DELETE_THROW +# if defined(__DMC__) || (defined(__BORLANDC__) \ + && (defined(_RWSTD_NO_EXCEPTIONS) || defined(_RWSTD_NO_EX_SPEC))) \ + || (defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS) \ + || (defined(__WATCOMC__) && !defined(_CPPUNWIND)) +# define GC_DECL_DELETE_THROW /* empty */ +# ifndef GC_NEW_ABORTS_ON_OOM +# define GC_NEW_ABORTS_ON_OOM +# endif +# else +# define GC_DECL_DELETE_THROW throw() +# endif +#endif // !GC_DECL_DELETE_THROW + +#if defined(GC_NEW_ABORTS_ON_OOM) || defined(_LIBCPP_NO_EXCEPTIONS) +# define GC_OP_NEW_OOM_CHECK(obj) \ + do { if (!(obj)) GC_abort_on_oom(); } while (0) +#elif defined(GC_INCLUDE_NEW) +# include // for bad_alloc +# define GC_OP_NEW_OOM_CHECK(obj) if (obj) {} else throw std::bad_alloc() +#else + // "new" header is not included, so bad_alloc cannot be thrown directly. + GC_API void GC_CALL GC_throw_bad_alloc(); +# define GC_OP_NEW_OOM_CHECK(obj) if (obj) {} else GC_throw_bad_alloc() +#endif // !GC_NEW_ABORTS_ON_OOM && !GC_INCLUDE_NEW + #ifdef GC_NAMESPACE namespace boehmgc { @@ -290,7 +316,9 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, # if _MSC_VER > 1020 inline void* operator new[](size_t size) { - return GC_MALLOC_UNCOLLECTABLE(size); + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } inline void operator delete[](void* obj) @@ -301,7 +329,9 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, inline void* operator new(size_t size) { - return GC_MALLOC_UNCOLLECTABLE(size); + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } inline void operator delete(void* obj) @@ -314,13 +344,17 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, inline void* operator new(size_t size, int /* nBlockUse */, const char* szFileName, int nLine) { - return GC_debug_malloc_uncollectable(size, szFileName, nLine); + void* obj = GC_debug_malloc_uncollectable(size, szFileName, nLine); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } # else inline void* operator new(size_t size, int /* nBlockUse */, const char* /* szFileName */, int /* nLine */) { - return GC_malloc_uncollectable(size); + void* obj = GC_malloc_uncollectable(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } # endif /* !GC_DEBUG */ @@ -351,24 +385,32 @@ namespace boehmgc inline void* gc::operator new(size_t size) { - return GC_MALLOC(size); + void* obj = GC_MALLOC(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } inline void* gc::operator new(size_t size, GCPlacement gcp) { + void* obj; switch (gcp) { case UseGC: - return GC_MALLOC(size); + obj = GC_MALLOC(size); + break; case PointerFreeGC: - return GC_MALLOC_ATOMIC(size); + obj = GC_MALLOC_ATOMIC(size); + break; # ifdef GC_ATOMIC_UNCOLLECTABLE case PointerFreeNoGC: - return GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); + obj = GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); + break; # endif case NoGC: default: - return GC_MALLOC_UNCOLLECTABLE(size); + obj = GC_MALLOC_UNCOLLECTABLE(size); } + GC_OP_NEW_OOM_CHECK(obj); + return obj; } inline void* gc::operator new(size_t /* size */, void* p) @@ -462,20 +504,24 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, switch (gcp) { case GC_NS_QUALIFY(UseGC): obj = GC_MALLOC(size); - if (cleanup != 0) { + if (cleanup != 0 && obj != 0) { GC_REGISTER_FINALIZER_IGNORE_SELF(obj, cleanup, clientData, 0, 0); } - return obj; + break; case GC_NS_QUALIFY(PointerFreeGC): - return GC_MALLOC_ATOMIC(size); + obj = GC_MALLOC_ATOMIC(size); + break; # ifdef GC_ATOMIC_UNCOLLECTABLE case GC_NS_QUALIFY(PointerFreeNoGC): - return GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); + obj = GC_MALLOC_ATOMIC_UNCOLLECTABLE(size); + break; # endif case GC_NS_QUALIFY(NoGC): default: - return GC_MALLOC_UNCOLLECTABLE(size); + obj = GC_MALLOC_UNCOLLECTABLE(size); } + GC_OP_NEW_OOM_CHECK(obj); + return obj; } #ifdef GC_PLACEMENT_DELETE diff --git a/misc.c b/misc.c index 21dc5474..a0b0bad8 100644 --- a/misc.c +++ b/misc.c @@ -2373,3 +2373,9 @@ GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void) { return (int)GC_force_unmap_on_gcollect; } + +GC_API void GC_CALL GC_abort_on_oom(void) +{ + GC_err_printf("Insufficient memory for the allocation\n"); + EXIT(); +} diff --git a/tests/test.c b/tests/test.c index 9e7a6c0c..758d03e3 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1921,6 +1921,7 @@ void GC_CALLBACK warn_proc(char *msg, GC_word p) UNTESTED(GetModuleNameFromStack); UNTESTED(GetSymbolNameFromStack); # endif + UNTESTED(GC_abort_on_oom); UNTESTED(GC_get_bytes_since_gc); UNTESTED(GC_get_dont_expand); UNTESTED(GC_get_dont_precollect);