https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121128

            Bug ID: 121128
           Summary: Dereferencing std::indirect causes use-after-free on a
                    valid code
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ykakeyama3014 at gmail dot com
  Target Milestone: ---

The code below does not compile:

#include <memory>

constexpr int func()
{
  std::indirect<int> x(42);
  return *std::move(x);
}

static_assert(func() == 42);

GCC says:

source.cpp:9:22: error: non-constant condition for static assertion
    9 | static_assert(func() == 42);
      |               ~~~~~~~^~~~~
source.cpp:9:19:   in ‘constexpr’ expansion of ‘func()’
source.cpp:6:10: error: use of allocated storage after deallocation in a
constant expression
    6 |   return *std::move(x);
      |          ^~~~~~~~~~~~~
In file included from /opt/gcc-trunk/include/c++/16.0.0/memory:67,
                 from source.cpp:1:
/opt/gcc-trunk/include/c++/16.0.0/bits/allocator.h:200:52: note: allocated here
  200 |             return static_cast<_Tp*>(::operator new(__n));
      |                                      ~~~~~~~~~~~~~~^~~~~

libstdc++'s implementation of operator* unnecessarily creates temporary copy of
__self, causing use-after-free.

template<typename _Self>
constexpr auto&&
operator*(this _Self&& __self) noexcept
{
  __glibcxx_assert(__self._M_objp != nullptr);
  return std::forward_like<_Self>(*((_Self)__self)._M_objp);
}

link:
https://github.com/gcc-mirror/gcc/blob/4d6c3f3b4fbf8c2774848fcb36705ea5f0d514d4/libstdc%2B%2B-v3/include/bits/indirect.h#L285-L291

It should be written as:

return std::forward_like<_Self>(*static_cast<_Self&&>(__self)._M_objp);

Note that _M_objp's type i.e. _ATraits::pointer may not be a raw pointer type,
so the value category should be propagated.

Reply via email to