]> granicus.if.org Git - gc/commitdiff
Never return null pointer by C++ operator new (gc_cpp)
authorIvan Maidanski <ivmai@mail.ru>
Fri, 1 Jun 2018 08:29:41 +0000 (11:29 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Mon, 16 Jul 2018 07:17:57 +0000 (10:17 +0300)
(a cherry-pick of commits cb1194d1e965562c4399253c2ce94e20 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).

gc_cpp.cc
include/gc.h
include/gc_cpp.h
misc.c
tests/test.c

index 103e66f8ed602d5f12bd7221f178df1be9ad2ba6..7d4a234088dfbde68f70cd33533bd98748c6a27d 100644 (file)
--- 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 <new> // 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 <new> /* 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 {
index bf795335f973416f44cd65a8b0ef52b085671ad0..da8202cd8d19e1b23a89233f0304148060b968bd 100644 (file)
@@ -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   */
index ed333af2cd437a3f111fb1dd6da547663358cc7e..ede55cdab60d30573d9607acb030568f5d773458 100644 (file)
@@ -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 <new> // 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 21dc5474fe1918e50b37eb124b242f115cb301b4..a0b0bad875459b5f16aa558a2be4340b76141468 100644 (file)
--- 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();
+}
index 9e7a6c0c9b74fe9c647965902ba4f7310effd181..758d03e3dd3c56c8799d888df77b4811a1bd2c5b 100644 (file)
@@ -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);