c++gcc-warninggcc12

gcc12 warning use-after-free with shared_ptr of type array


While updating my gcc compiler to gcc12.x version I came across some new warnings.

Consider this piece of code:

#include <memory>

struct Test
{
    inline static int i = 0;
    Test() { ++i; }
    ~Test() { --i; }
};

struct MHeap
{
    template<typename T>
    void manage() 
    {
        using Td = std::remove_extent_t<T>;
        std::shared_ptr<T> ptr(new T(), std::default_delete<Td[]>{});
    }
};

int main()
{
    MHeap mem;
    mem.manage<Test[3]>();
}

$ g++ -O2 -Wall main.cpp

Error:

In member function 'void MHeap::manage() [with T = Test [3]]',
    inlined from 'int main()' at <source>:24:25:
<source>:17:32: warning: pointer used after 'void operator delete [](void*, std::size_t)' [-Wuse-after-free]
   17 |         std::shared_ptr<T> ptr(new T(), std::default_delete<Td[]>{});
      |                                ^~~~~~~
In file included from /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/memory:75,
                 from <source>:2:
In member function 'typename std::enable_if<std::is_convertible<_Up (*)[], _Tp (*)[]>::value>::type std::default_delete<_Tp []>::operator()(_Up*) const [with _Up = Test; _Tp = Test]',
    inlined from 'std::__shared_count<_Lp>::__shared_count(_Ptr, _Deleter, _Alloc) [with _Ptr = Test*; _Deleter = std::default_delete<Test []>; _Alloc = std::allocator<void>; <template-parameter-2-4> = void; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:958:11,
    inlined from 'std::__shared_count<_Lp>::__shared_count(_Ptr, _Deleter) [with _Ptr = Test*; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:939:57,
    inlined from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*, _Deleter) [with _Yp = Test; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; _Tp = Test [3]; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:1478:17,
    inlined from 'std::shared_ptr<_Tp>::shared_ptr(_Yp*, _Deleter) [with _Yp = Test; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; _Tp = Test [3]]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr.h:232:48,
    inlined from 'void MHeap::manage() [with T = Test [3]]' at <source>:17:28,
    inlined from 'int main()' at <source>:24:25:
/opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/unique_ptr.h:132:11: note: call to 'void operator delete [](void*, std::size_t)' here
  132 |           delete [] __ptr;
      |           ^~~~~~~~~~~~~~~

Is this warning valid ?

Compiler: GCC 12

Interestingly using a array of size less than 3 i.e mem.manage<Test[2]>() clears the warning.


Solution

  • (Don't have enough reputation to comment heh) Following on Remy's answer and Artyer's comment, one can see on godbolt that the warning disappears if one slightly refactors the code:

    using Td = std::remove_extent_t<T>;
    auto p = new T;
    std::shared_ptr<T> ptr(p, std::default_delete<Td[]>{});
    

    I'm not sure why this happens.