]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Reduce chances of object aliasing for function wrapper.
authorTomasz Kamiński <tkaminsk@redhat.com>
Tue, 19 Aug 2025 14:44:49 +0000 (16:44 +0200)
committerTomasz Kamiński <tkaminsk@redhat.com>
Wed, 27 Aug 2025 04:35:04 +0000 (06:35 +0200)
Previously, an empty functor (EmptyIdFunc) stored inside a
std::move_only_function being first member of a Composite class could have the
same address as the base of the EmptyIdFunc type (see included test cases),
resulting in two objects of the same type at the same address.

This commit addresses the issue by moving the internal buffer from the start
of the wrapper object to a position after the manager function pointer. This
minimizes aliasing with the stored buffer but doesn't completely eliminate it,
especially when multiple empty base objects are involved (PR121180).

To facilitate this member reordering, the private section of _Mo_base was
eliminated, and the corresponding _M_manager and _M_destroy members were made
protected. They remain inaccessible to users, as user-facing wrappers derive
from _Mo_base privately.

libstdc++-v3/ChangeLog:

* include/bits/funcwrap.h (__polyfunc::_Mo_base): Reorder _M_manage
and _M_storage members. Make _M_destroy protected and remove friend
declaration.
* testsuite/20_util/copyable_function/call.cc: Add test for aliasing
base class.
* testsuite/20_util/move_only_function/call.cc: Likewise.

Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Reviewed-by: Patrick Palka <ppalka@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
libstdc++-v3/include/bits/funcwrap.h
libstdc++-v3/testsuite/20_util/copyable_function/call.cc
libstdc++-v3/testsuite/20_util/move_only_function/call.cc

index 9db4ab7811a88658c6e2c2fcb3342e496b363ebd..70ecfd93e369d17f915b6464c7d3632b622eada0 100644 (file)
@@ -419,6 +419,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _M_manage = _Manager::_S_empty;
      }
 
+     void _M_destroy() noexcept
+     { _M_manage(_Manager::_Op::_Destroy, _M_storage, nullptr); }
+
      ~_Mo_base()
      { _M_destroy(); }
 
@@ -434,17 +437,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        std::swap(_M_manage, __x._M_manage);
      }
 
-     _Storage _M_storage;
-
-   private:
-     void _M_destroy() noexcept
-     { _M_manage(_Manager::_Op::_Destroy, _M_storage, nullptr); }
-
      _Manager::_Func _M_manage;
-
-#ifdef __glibcxx_copyable_function // C++ >= 26 && HOSTED
-     friend class _Cpy_base;
-#endif // __glibcxx_copyable_function
+     _Storage _M_storage;
    };
 #endif // __glibcxx_copyable_function || __glibcxx_copyable_function
 } // namespace __polyfunc
index 0ac5348f2fe19d68c666b86eb984191a63a3d5b7..605422d55953231ddfb7cf33678264480124fedd 100644 (file)
@@ -214,6 +214,28 @@ test_params()
   std::copyable_function<void(CompleteEnum)> f4;
 }
 
+struct EmptyIdFunc
+{
+  EmptyIdFunc* operator()()
+  { return this; }
+};
+
+struct Composed : EmptyIdFunc
+{
+  std::move_only_function<EmptyIdFunc*()> nested;
+};
+
+void
+test_aliasing()
+{
+  Composed c;
+  c.nested = EmptyIdFunc{};
+
+  EmptyIdFunc* baseAddr = c();
+  EmptyIdFunc* nestedAddr = c.nested();
+  VERIFY( baseAddr != nestedAddr );
+};
+
 int main()
 {
   test01();
@@ -222,4 +244,5 @@ int main()
   test04();
   test05();
   test_params();
+  test_aliasing();
 }
index 72c8118d716eacffbfe7f988cc6d211d301bc6f5..34ca73b4eb41543c3cde554d4ffd11495715cb81 100644 (file)
@@ -214,6 +214,28 @@ test_params()
   std::move_only_function<void(CompleteEnum)> f4;
 }
 
+struct EmptyIdFunc
+{
+  EmptyIdFunc* operator()()
+  { return this; }
+};
+
+struct Composed : EmptyIdFunc
+{
+  std::move_only_function<EmptyIdFunc*()> nested;
+};
+
+void
+test_aliasing()
+{
+  Composed c;
+  c.nested = EmptyIdFunc{};
+
+  EmptyIdFunc* baseAddr = c();
+  EmptyIdFunc* nestedAddr = c.nested();
+  VERIFY( baseAddr != nestedAddr );
+};
+
 int main()
 {
   test01();
@@ -222,4 +244,5 @@ int main()
   test04();
   test05();
   test_params();
+  test_aliasing();
 }