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