[PATCH] PR libstdc++/87520 Always pass type-punned type_info reference

Jonathan Wakely jwakely@redhat.com
Thu Nov 22 13:42:00 GMT 2018


The implementations of std::make_shared for -frtti and -fno-rtti are not
compatible, because they pass different arguments to
_Sp_counted_ptr_inplace::_M_get_deleter and so can't interoperate.
Either the argument doesn't match the expected value, and so the
shared_ptr::_M_ptr member is never set, or the type-punned reference is
treated as a real std::type_info object and gets dereferenced.

This patch removes the differences between -frtti and -fno-rtti, so that
typeid is never used, and the type-punned reference is used in both
cases. For backwards compatibility with existing code that passes
typeid(_Sp_make_shared_tag) that still needs to be handled, but only
after checking that the argument is not the type-punned reference (so
it's safe to treat as a real std::type_info object). The reference is
bound to an object of literal type, so that it doesn't need a guard
variable to make its initialization thread-safe.

This patch also fixes 87520 by ensuring that the type-punned reference
is bound to "a region of storage of suitable size and alignment to
contain an object of the reference's type" (as per the proposed
resolution of Core DR 453).

If all objects are built with the fixed version of GCC then -frtti and
-fno-rtti can be mixed freely and std::make_shared will work correctly.
If some objects are built with unfixed GCC versions then problems can
still arise, depending on which template instantiations are kept by the
linker.

	PR libstdc++/85930
	PR libstdc++/87520
	* include/bits/shared_ptr_base.h (_Sp_make_shared_tag::_S_ti)
	[__cpp_rtti]: Define even when RTTI is enabled. Use array of
	sizeof(type_info) so that type-punned reference binds to an object
	of the correct size as well as correct alignment.
	(_Sp_counted_ptr_inplace::_M_get_deleter) [__cpp_rtti]: Check for
	_S_ti() reference even when RTTI is enabled.
	(__shared_ptr(_Sp_make_shared_tag, const _Alloc&, _Args&&...))
	[__cpp_rtti]: Pass _S_ti() instead of typeid(_Sp_make_shared_tag).

Tested x86_64-linux, committed to trunk. Backport to gcc-8-branch will
follow soon.


-------------- next part --------------
commit c3eb187b5da75f0df004e49f3ecad7be906a0231
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Oct 4 16:16:57 2018 +0100

    PR libstdc++/87520 Always pass type-punned type_info reference
    
    The implementations of std::make_shared for -frtti and -fno-rtti are not
    compatible, because they pass different arguments to
    _Sp_counted_ptr_inplace::_M_get_deleter and so can't interoperate.
    Either the argument doesn't match the expected value, and so the
    shared_ptr::_M_ptr member is never set, or the type-punned reference is
    treated as a real std::type_info object and gets dereferenced.
    
    This patch removes the differences between -frtti and -fno-rtti, so that
    typeid is never used, and the type-punned reference is used in both
    cases. For backwards compatibility with existing code that passes
    typeid(_Sp_make_shared_tag) that still needs to be handled, but only
    after checking that the argument is not the type-punned reference (so
    it's safe to treat as a real std::type_info object). The reference is
    bound to an object of literal type, so that it doesn't need a guard
    variable to make its initialization thread-safe.
    
    This patch also fixes 87520 by ensuring that the type-punned reference
    is bound to "a region of storage of suitable size and alignment to
    contain an object of the reference's type" (as per the proposed
    resolution of Core DR 453).
    
    If all objects are built with the fixed version of GCC then -frtti and
    -fno-rtti can be mixed freely and std::make_shared will work correctly.
    If some objects are built with unfixed GCC versions then problems can
    still arise, depending on which template instantiations are kept by the
    linker.
    
            PR libstdc++/85930
            PR libstdc++/87520
            * include/bits/shared_ptr_base.h (_Sp_make_shared_tag::_S_ti)
            [__cpp_rtti]: Define even when RTTI is enabled. Use array of
            sizeof(type_info) so that type-punned reference binds to an object
            of the correct size as well as correct alignment.
            (_Sp_counted_ptr_inplace::_M_get_deleter) [__cpp_rtti]: Check for
            _S_ti() reference even when RTTI is enabled.
            (__shared_ptr(_Sp_make_shared_tag, const _Alloc&, _Args&&...))
            [__cpp_rtti]: Pass _S_ti() instead of typeid(_Sp_make_shared_tag).

diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h
index 870aeb9bfda..46ff4a7cf29 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -500,7 +500,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   struct _Sp_make_shared_tag
   {
-#if !__cpp_rtti
   private:
     template<typename _Tp, _Lock_policy _Lp>
       friend class __shared_ptr;
@@ -510,10 +509,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     static const type_info&
     _S_ti() noexcept _GLIBCXX_VISIBILITY(default)
     {
-      alignas(type_info) static constexpr _Sp_make_shared_tag __tag;
+      alignas(type_info) static constexpr char __tag[sizeof(type_info)] = { };
       return reinterpret_cast<const type_info&>(__tag);
     }
-#endif
   };
 
   template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
@@ -562,16 +560,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	this->~_Sp_counted_ptr_inplace();
       }
 
-      // Sneaky trick so __shared_ptr can get the managed pointer
+      // Sneaky trick so __shared_ptr can get the managed pointer.
       virtual void*
-      _M_get_deleter(const std::type_info& __ti) noexcept
+      _M_get_deleter(const std::type_info& __ti) noexcept override
       {
-#if __cpp_rtti
-	if (__ti == typeid(_Sp_make_shared_tag))
-#else
+	// Check for the fake type_info first, so we don't try to access it
+	// as a real type_info object.
 	if (&__ti == &_Sp_make_shared_tag::_S_ti())
-#endif
 	  return const_cast<typename remove_cv<_Tp>::type*>(_M_ptr());
+#if __cpp_rtti
+	// Callers compiled with old libstdc++ headers and RTTI enabled
+	// might pass this instead:
+	else if (__ti == typeid(_Sp_make_shared_tag))
+	  return const_cast<typename remove_cv<_Tp>::type*>(_M_ptr());
+#endif
 	return nullptr;
       }
 
@@ -1323,11 +1325,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  // _M_ptr needs to point to the newly constructed object.
 	  // This relies on _Sp_counted_ptr_inplace::_M_get_deleter.
-#if __cpp_rtti
-	  void* __p = _M_refcount._M_get_deleter(typeid(__tag));
-#else
 	  void* __p = _M_refcount._M_get_deleter(_Sp_make_shared_tag::_S_ti());
-#endif
 	  _M_ptr = static_cast<_Tp*>(__p);
 	  _M_enable_shared_from_this_with(_M_ptr);
 	}


More information about the Gcc-patches mailing list