Am I accessing already freed memory, or is DrMemory reporting incorrectly in this case?

359 Views Asked by At

I have the following program:

#include <vector>

struct Comp1
{
    float x;
    std::vector<int> vec;
};

int main()
{
    std::vector<char> data;

    data.resize(sizeof(Comp1));

    Comp1* ptr1 = new (&data[0]) Comp1({.3f,{3,4,2,1}});

    data.resize(sizeof(Comp1)*2);

    Comp1* ptr2 = new (&data[sizeof(Comp1)]) Comp1({.2f,{2,3,4}});

    ptr1->~Comp1();

    std::vector<char>::iterator itrStart = data.begin();
    std::vector<char>::iterator itrEnd = itrStart + sizeof(Comp1);
    data.erase(itrStart,itrEnd);

    ptr2 = reinterpret_cast<Comp1*>(&data[0]);

    ptr2->~Comp1();

    return 0;
}

Compiles with no issue. Executes on my machine with no issue. Run it through GDB and there's nothing reported. My understanding is that everything I've done in the above is correct (except maybe I'm missing the use of std::launder when I reinterpret_cast from a char* to a Comp1*?).

However, invoking DrMemory on this program with:

drmemory.exe test.exe -callstack_max_frames 40 -malloc_max_frames 40 -free_max_frames 40

Gives me the following error output:

Dr. Memory version 2.2.0 build 1 built on Jul  1 2019 00:42:20
Windows version: WinVer=105;Rel=1909;Build=18363;Edition=Enterprise
Dr. Memory results for pid 20424: "test.exe"
Application cmdline: "bin/test.exe -callstack_max_frames 40 -malloc_max_frames 40 -free_max_frames 40"
Recorded 117 suppression(s) from default C:\Program Files (x86)\Dr. Memory\bin\suppress-default.txt

Error #1: UNADDRESSABLE ACCESS of freed memory: reading 0x01975230-0x01975234 4 byte(s)
# 0 std::vector<>::~vector               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:680]
# 1 Comp1::~Comp1                        [C:/Tests/test.cpp:3]
# 2 main                                 [C:/Tests/test.cpp:21]
Note: @0:00:01.329 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x01975230-0x01975234 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    0x04(%edx) -> %ecx

Error #2: UNADDRESSABLE ACCESS of freed memory: reading 0x0197522c-0x01975230 4 byte(s)
# 0 std::vector<>::~vector               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:680]
# 1 Comp1::~Comp1                        [C:/Tests/test.cpp:3]
# 2 main                                 [C:/Tests/test.cpp:21]
Note: @0:00:01.447 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x0197522c-0x01975230 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    (%edx) -> %edx

Error #3: UNADDRESSABLE ACCESS of freed memory: reading 0x01975234-0x01975238 4 byte(s)
# 0 std::_Vector_base<>::~_Vector_base               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:336]
# 1 std::vector<>::~vector                           [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:683]
# 2 Comp1::~Comp1                                    [C:/Tests/test.cpp:3]
# 3 main                                             [C:/Tests/test.cpp:21]
Note: @0:00:01.471 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x01975234-0x01975238 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    0x08(%eax) -> %edx

Error #4: UNADDRESSABLE ACCESS of freed memory: reading 0x0197522c-0x01975230 4 byte(s)
# 0 std::_Vector_base<>::~_Vector_base               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:336]
# 1 std::vector<>::~vector                           [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:683]
# 2 Comp1::~Comp1                                    [C:/Tests/test.cpp:3]
# 3 main                                             [C:/Tests/test.cpp:21]
Note: @0:00:01.489 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x0197522c-0x01975230 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    (%eax) -> %ecx

Error #5: UNADDRESSABLE ACCESS of freed memory: reading 0x0197522c-0x01975230 4 byte(s)
# 0 std::_Vector_base<>::~_Vector_base               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:335]
# 1 std::vector<>::~vector                           [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:683]
# 2 Comp1::~Comp1                                    [C:/Tests/test.cpp:3]
# 3 main                                             [C:/Tests/test.cpp:21]
Note: @0:00:01.504 in thread 2068
Note: next higher malloc: 0x01975258-0x01975268
Note: 0x0197522c-0x01975230 overlaps memory 0x01975228-0x01975238 that was freed here:
Note: # 0 replace_operator_delete                              [d:\drmemory_package\common\alloc_replace.c:2975]
Note: # 1 __gnu_cxx::new_allocator<>::deallocate               [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
Note: # 2 std::allocator_traits<>::deallocate                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
Note: # 3 std::_Vector_base<>::_M_deallocate                   [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:354]
Note: # 4 std::vector<>::_M_default_append                     [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/vector.tcc:675]
Note: # 5 std::vector<>::resize                                [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/stl_vector.h:940]
Note: instruction: mov    (%eax) -> %edx

===========================================================================
FINAL SUMMARY:

DUPLICATE ERROR COUNTS:

SUPPRESSIONS USED:

ERRORS FOUND:
      5 unique,     5 total unaddressable access(es)
      0 unique,     0 total uninitialized access(es)
      0 unique,     0 total invalid heap argument(s)
      0 unique,     0 total GDI usage error(s)
      0 unique,     0 total handle leak(s)
      0 unique,     0 total warning(s)
      0 unique,     0 total,      0 byte(s) of leak(s)
      0 unique,     0 total,      0 byte(s) of possible leak(s)
ERRORS IGNORED:
      2 potential error(s) (suspected false positives)
         (details: C:\Users\david\AppData\Roaming\Dr. Memory\DrMemory-test.exe.20424.000\potential_errors.txt)
      8 unique,    14 total,  19443 byte(s) of still-reachable allocation(s)
         (re-run with "-show_reachable" for details)
Details: C:\Users\david\AppData\Roaming\Dr. Memory\DrMemory-test.exe.20424.000\results.txt

Have I missed something with my program, or is this a case where DrMemory can't really work out what I'm doing?

2

There are 2 best solutions below

0
On BEST ANSWER

In your code you do

data.resize(sizeof(Comp1)*2);

which is a modification event of the storage the vector uses. When that happens, all pointers, iterators, and references into the vector are invalidated. That means

ptr1->~Comp1();

is accessing an invalid object and you have undefined behavior. This isn't a memory leak, but an illegal access issue and that is what Dr. Memory is diagnosing.

0
On

(Too long for a comment.)

If you want the backing store buffer of the in-place constructed objects not to move around, you can use a vector of vector of bytes. If you want consecutive memory though (say, for cache locality), this won't work.

#include <vector>

using std::vector;

struct Comp1 {
    float x;
    vector<int> vec;
};

int main() {
    vector<vector<char>> data;

    data.push_back(vector<char>(sizeof(Comp1)));
    Comp1* ptr1 = new (&data[0][0]) Comp1({.3f,{3,4,2,1}});

    data.push_back(vector<char>(sizeof(Comp1)));

    Comp1* ptr2 = new (&data[1][0]) Comp1({.2f,{2,3,4}});

    ptr1->~Comp1();

    auto itrStart = data.begin();
    auto itrEnd = itrStart + 1;
    data.erase(itrStart, itrEnd);

    ptr2->~Comp1();
}