From cb1194d17e4eb20a86b556ba7940a151a7a42a61 Mon Sep 17 00:00:00 2001 From: Ivan Maidanski Date: Fri, 1 Jun 2018 11:29:41 +0300 Subject: [PATCH] Never return null pointer by C++ operator new (gc_cpp) 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 (GC_NEW_DELETE_NEED_THROW): Remove. * gc_cpp.cc (GC_DECL_NEW_THROW, GC_DECL_DELETE_THROW): Move macro definition to gc_cpp.h. * gc_cpp.cc [GC_NEW_DELETE_NEED_THROW]: Do not include "new" header. * gc_cpp.cc [!_MSC_VER] (operator new): Call GC_OP_NEW_OOM_CHECK() for the allocation result. * gc_cpp.cc [!_MSC_VER && GC_OPERATOR_NEW_ARRAY && !CPPCHECK] (operator new[]): Likewise. * include/gc.h (GC_abort_on_oom): Declare new API function. * include/gc_cpp.h [!GC_NEW_DELETE_THROW_NOT_NEEDED && (GC_GNUC_PREREQ(4,2) || __BORLANDC__>=0x0550 || _MSC_VER>1020 || __WATCOMC__>=1050)] (GC_NEW_DELETE_NEED_THROW): Define macro. * include/gc_cpp.h [GC_NEW_DELETE_NEED_THROW]: Include "new" header. * 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 (gc::new(size_t), gc::new(size_t,GCPlacement), new): Add GC_DECL_NEW_THROW; 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). --- gc_cpp.cc | 21 ++++-------- include/gc.h | 3 ++ include/gc_cpp.h | 99 ++++++++++++++++++++++++++++++++++++++++---------------- misc.c | 6 ++++ tests/test.c | 1 + 5 files changed, 88 insertions(+), 42 deletions(-) diff --git a/gc_cpp.cc b/gc_cpp.cc index 80a9708..73f54cf 100644 --- a/gc_cpp.cc +++ b/gc_cpp.cc @@ -29,23 +29,12 @@ built-in "new" and "delete". #include "gc_cpp.h" -#if GC_GNUC_PREREQ(4, 2) && !defined(GC_NEW_DELETE_NEED_THROW) -# define GC_NEW_DELETE_NEED_THROW -#endif - -#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() -#else -# define GC_DECL_NEW_THROW /* empty */ -# define GC_DECL_DELETE_THROW /* empty */ -#endif // !GC_NEW_DELETE_NEED_THROW - #ifndef _MSC_VER void* operator new(size_t size) GC_DECL_NEW_THROW { - return GC_MALLOC_UNCOLLECTABLE(size); + void* obj = GC_MALLOC_UNCOLLECTABLE(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } void operator delete(void* obj) GC_DECL_DELETE_THROW { @@ -54,7 +43,9 @@ 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); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } void operator delete[](void* obj) GC_DECL_DELETE_THROW { diff --git a/include/gc.h b/include/gc.h index ff05128..0f5d843 100644 --- a/include/gc.h +++ b/include/gc.h @@ -1343,6 +1343,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 875c64e..091e709 100644 --- a/include/gc_cpp.h +++ b/include/gc_cpp.h @@ -173,6 +173,26 @@ by UseGC. GC is an alias for UseGC, unless GC_NAME_CONFLICT is defined. # define GC_PLACEMENT_DELETE #endif +#if !defined(GC_NEW_DELETE_THROW_NOT_NEEDED) \ + && !defined(GC_NEW_DELETE_NEED_THROW) \ + && (GC_GNUC_PREREQ(4, 2) || __BORLANDC__ >= 0x0550 \ + || _MSC_VER > 1020 || __WATCOMC__ >= 1050) +# define GC_NEW_DELETE_NEED_THROW +#endif + +#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() +# define GC_OP_NEW_OOM_CHECK(obj) \ + do { if (!(obj)) throw std::bad_alloc(); } while (0) +#else +# define GC_DECL_NEW_THROW /* empty */ +# define GC_DECL_DELETE_THROW /* empty */ +# define GC_OP_NEW_OOM_CHECK(obj) \ + do { if (!(obj)) GC_abort_on_oom(); } while (0) +#endif // !GC_NEW_DELETE_NEED_THROW + #ifdef GC_NAMESPACE namespace boehmgc { @@ -198,8 +218,8 @@ enum GCPlacement class gc { public: - inline void* operator new(size_t size); - inline void* operator new(size_t size, GCPlacement gcp); + inline void* operator new(size_t size) GC_DECL_NEW_THROW; + inline void* operator new(size_t size, GCPlacement gcp) GC_DECL_NEW_THROW; inline void* operator new(size_t size, void* p); // Must be redefined here, since the other overloadings hide // the global definition. @@ -212,8 +232,9 @@ public: # endif // GC_PLACEMENT_DELETE # ifdef GC_OPERATOR_NEW_ARRAY - inline void* operator new[](size_t size); - inline void* operator new[](size_t size, GCPlacement gcp); + inline void* operator new[](size_t size) GC_DECL_NEW_THROW; + inline void* operator new[](size_t size, GCPlacement gcp) + GC_DECL_NEW_THROW; inline void* operator new[](size_t size, void* p); inline void operator delete[](void* obj); # ifdef GC_PLACEMENT_DELETE @@ -255,7 +276,7 @@ extern "C" { inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */ = 0, - void* /* clientData */ = 0); + void* /* clientData */ = 0) GC_DECL_NEW_THROW; // Allocates a collectible or uncollectible object, according to the // value of "gcp". // @@ -284,9 +305,11 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, // to arbitrary ordering during linking). # if _MSC_VER > 1020 - inline void* operator new[](size_t size) + inline void* operator new[](size_t size) GC_DECL_NEW_THROW { - 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) @@ -295,9 +318,11 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, } # endif - inline void* operator new(size_t size) + inline void* operator new(size_t size) GC_DECL_NEW_THROW { - 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) @@ -309,14 +334,20 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, # ifdef GC_DEBUG inline void* operator new(size_t size, int /* nBlockUse */, const char* szFileName, int nLine) + GC_DECL_NEW_THROW { - 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 */) + GC_DECL_NEW_THROW { - return GC_malloc_uncollectable(size); + void* obj = GC_malloc_uncollectable(size); + GC_OP_NEW_OOM_CHECK(obj); + return obj; } # endif /* !GC_DEBUG */ @@ -324,6 +355,7 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, // This new operator is used by VC++ 7+ in Debug builds: inline void* operator new[](size_t size, int nBlockUse, const char* szFileName, int nLine) + GC_DECL_NEW_THROW { return operator new(size, nBlockUse, szFileName, nLine); } @@ -335,7 +367,7 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, // The operator new for arrays, identical to the above. inline void* operator new[](size_t size, GC_NS_QUALIFY(GCPlacement) gcp, GC_NS_QUALIFY(GCCleanUpFunc) /* cleanup */ = 0, - void* /* clientData */ = 0); + void* /* clientData */ = 0) GC_DECL_NEW_THROW; #endif // GC_OPERATOR_NEW_ARRAY /* Inline implementation */ @@ -345,26 +377,34 @@ namespace boehmgc { #endif -inline void* gc::operator new(size_t size) +inline void* gc::operator new(size_t size) GC_DECL_NEW_THROW { - 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) +inline void* gc::operator new(size_t size, GCPlacement gcp) GC_DECL_NEW_THROW { + 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) @@ -387,12 +427,13 @@ inline void gc::operator delete(void* obj) #endif // GC_PLACEMENT_DELETE #ifdef GC_OPERATOR_NEW_ARRAY - inline void* gc::operator new[](size_t size) + inline void* gc::operator new[](size_t size) GC_DECL_NEW_THROW { return gc::operator new(size); } inline void* gc::operator new[](size_t size, GCPlacement gcp) + GC_DECL_NEW_THROW { return gc::operator new(size, gcp); } @@ -452,26 +493,30 @@ inline gc_cleanup::gc_cleanup() inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, GC_NS_QUALIFY(GCCleanUpFunc) cleanup, - void* clientData) + void* clientData) GC_DECL_NEW_THROW { void* obj; 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 @@ -486,7 +531,7 @@ inline void* operator new(size_t size, GC_NS_QUALIFY(GCPlacement) gcp, #ifdef GC_OPERATOR_NEW_ARRAY inline void* operator new[](size_t size, GC_NS_QUALIFY(GCPlacement) gcp, GC_NS_QUALIFY(GCCleanUpFunc) cleanup, - void* clientData) + void* clientData) GC_DECL_NEW_THROW { return ::operator new(size, gcp, cleanup, clientData); } diff --git a/misc.c b/misc.c index 7d0f466..c91c788 100644 --- a/misc.c +++ b/misc.c @@ -2516,3 +2516,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 4700a8c..69fd7bd 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1882,6 +1882,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); -- 2.7.4