The test case in PR 99074 invokes dynamic_cast with the this pointer
in a non-static member function called on a null pointer.  The call
is, of course, undefined and other different circumstances would be
diagnosed by -Wnonnull.   Unfortunately, in the test case, the null
pointer is the result of inlining and constant propagation and so
detected neither by the front end -Wnonnull nor by the middle end.
The program ends up passing it to __dynamic_cast() which then
crashes at runtime (again, not surprising for undefined behavior.

However, the reporter says the code behaved gracefully (didn't crash)
when compiled with GCC 4.8, and in my tests it also doesn't crash
when compiled with Clang or ICC.  I looked to see if it's possible
to do better and it seems it is.

The attached patch improves things by changing __dynamic_cast to
fail by returning null when the first argument is null, and also
by declaring __dynamic_cast with attribute nonnull so that invalid
calls to it with a constant null pointer can be detected at compile
time.

Since the test case is undefined it seems borderline whether this
can strictly be considered a regression, even if some previous
releases handled it more gracefully.  I post it for consideration
either now or for GCC 12.

Tested on x86_64-linux.

Martin
PR c++/99074 - crash in dynamic_cast<>() on null pointer

gcc/cp/ChangeLog:

	PR c++/99074
	* rtti.c (build_dynamic_cast_1): Declare nonnull.

libstdc++-v3/ChangeLog:

	PR c++/99074
	* libsupc++/dyncast.cc (__dynamic_cast): Return null when
	first argument is null.

gcc/testsuite/ChangeLog:

	PR c++/99074
	* g++.dg/warn/Wnonnull11.C: New test.

diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index b41d95469c6..b78b5f2dab4 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "c-family/c-pragma.h"
 #include "gcc-rich-location.h"
+#include "attribs.h"
 
 /* C++ returns type information to the user in struct type_info
    objects. We also use type information to implement dynamic_cast and
@@ -767,6 +768,11 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
 	      dcast_fn = (build_library_fn_ptr
 			  (fn_name, fn_type, ECF_LEAF | ECF_PURE | ECF_NOTHROW));
 	      pop_abi_namespace (flags);
+
+	      /* __dynamic_cast expects all nonnull pointers.  */
+	      tree attr = tree_cons (get_identifier ("nonnull"),
+				     NULL_TREE, NULL_TREE);
+	      decl_attributes (&dcast_fn, attr, 0);
 	      dynamic_cast_node = dcast_fn;
 	    }
 	  result = build_cxx_call (dcast_fn, 4, elems, complain);
diff --git a/gcc/testsuite/g++.dg/warn/Wnonnull11.C b/gcc/testsuite/g++.dg/warn/Wnonnull11.C
new file mode 100644
index 00000000000..1dcd820356a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wnonnull11.C
@@ -0,0 +1,40 @@
+/* PR c++/99074 - gcc 8 and above is crashing with dynamic_cast<>() on null
+   pointer with optimization level -O1 and above
+   { dg-do run }
+   { dg-options "-O1 -Wall" } */
+
+class Base
+{
+public:
+  virtual ~Base() {}
+  virtual void op() = 0;
+};
+
+class Object: public virtual Base { };
+
+class AbstractBase: public virtual Base
+{
+public:
+  Object* _to_object ()
+  {
+    return dynamic_cast<Object*>(this);   // { dg-warning "\\\[-Wnonnull" }
+  }
+};
+
+class MyAbstractClass: public virtual AbstractBase
+{
+public:
+  static MyAbstractClass* _nil () { return 0; }
+};
+
+
+int main ()
+{
+  MyAbstractClass *my_abs_type = MyAbstractClass::_nil ();
+  AbstractBase *abs_base = my_abs_type;
+  Object *obj = abs_base->_to_object ();
+
+  __builtin_printf ("object is: %p\n", obj);
+
+  return 0;
+}
diff --git a/libstdc++-v3/libsupc++/dyncast.cc b/libstdc++-v3/libsupc++/dyncast.cc
index b7d98495ad3..f8f707ee4d4 100644
--- a/libstdc++-v3/libsupc++/dyncast.cc
+++ b/libstdc++-v3/libsupc++/dyncast.cc
@@ -47,6 +47,9 @@ __dynamic_cast (const void *src_ptr,    // object started from
                 const __class_type_info *dst_type, // desired target type
                 ptrdiff_t src2dst) // how src and dst are related
   {
+  if (!src_ptr)
+    /* Handle precondition violations gracefully.  */
+    return NULL;
   const void *vtable = *static_cast <const void *const *> (src_ptr);
   const vtable_prefix *prefix =
     (adjust_pointer <vtable_prefix>

Reply via email to