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

123 views Asked by At

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.

2

There are 2 answers

1
ljudek On BEST ANSWER

(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.

1
Artyer On

This seems like a false positive, your code should not use-after-free. This warning goes away when you again upgrade to gcc13.

As a workaround you can use this function:

template<typename T>
std::shared_ptr<T> make_shared_fixed_array() {
    static_assert(std::extent_v<T> != 0, "Must be an array type");
    struct holder { T array; };
    auto sp = std::make_shared<holder>();
    return std::shared_ptr<T>(std::move(sp), sp->array);
}

// ...

    template<typename T>
    void manage() 
    {
        std::shared_ptr<T> ptr = make_shared_fixed_array<T>();
    }

Which only indirectly uses an array type, doesn't use delete[], and seems to confuse gcc12 less. It has the added benefit of being slightly more efficient (since the size is not also stored at runtime).