1

The following code

#include <vector>
#include <string>
#include <iostream>

struct Type
{
    std::string a;
};

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

    data.resize(sizeof(Type));

    Type* a = new (&data[0]) Type({"something"});

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

    Type* b = new (&data[sizeof(Type)]) Type({"else"});

    Type* ts = reinterpret_cast<Type*>(&data[0]);

    for(std::size_t i = 0; i < 2; ++i)
    {
        std::cout << ts[i].a << std::endl;
        ts[i].~Type();
    }

    return 0;
}

produces this output from Dr Memory

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 14664: "CPPTests.exe"
Application cmdline: "bin/Debug/CPPTests.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 0x01750568-0x01750569 1 byte(s)
# 0 msvcrt.dll!_fwrite_nolock
# 1 msvcrt.dll!fwrite    
# 2 libstdc++-6.dll!?                +0x0      (0x6fe5cf56 <libstdc++-6.dll+0x1cf56>)
# 3 libstdc++-6.dll!?                +0x0      (0x6ff23d80 <libstdc++-6.dll+0xe3d80>)
# 4 _fu0___ZSt4cout                   [C:/tests.cpp:26]
# 5 __tmainCRTStartup                 [D:/mingwbuild/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:335]
# 6 KERNEL32.dll!BaseThreadInitThunk +0x18     (0x75306359 <KERNEL32.dll+0x16359>)
Note: @0:00:00.608 in thread 3732
Note: next higher malloc: 0x01750598-0x017505c8
Note: prev lower malloc:  0x01750468-0x0175046b
Note: 0x01750568-0x01750569 overlaps memory 0x01750560-0x01750578 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: movsx  (%eax) -> %eax

Error #2: INVALID HEAP ARGUMENT to free 0x01750568
# 0 replace_operator_delete                                   [d:\drmemory_package\common\alloc_replace.c:2975]
# 1 __gnu_cxx::new_allocator<>::deallocate                    [C:/MSYS2-32/mingw32/include/c++/10.1.0/ext/new_allocator.h:133]
# 2 std::allocator_traits<>::deallocate                       [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/allocator.h:187]
# 3 std::__cxx11::basic_string<>::_M_destroy                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/basic_string.h:237]
# 4 std::__cxx11::basic_string<>::_M_dispose                  [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/basic_string.h:232]
# 5 std::__cxx11::basic_string<>::~basic_string               [C:/MSYS2-32/mingw32/include/c++/10.1.0/bits/basic_string.h:658]
# 6 Type::~Type                                               [C:/tests.cpp:5]
# 7 _fu0___ZSt4cout                                           [C:/tests.cpp:27]
# 8 __tmainCRTStartup                                         [D:/mingwbuild/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:335]
# 9 KERNEL32.dll!BaseThreadInitThunk                         +0x18     (0x75306359 <KERNEL32.dll+0x16359>)
Note: @0:00:00.743 in thread 3732
Note: next higher malloc: 0x01750598-0x017505c8
Note: prev lower malloc:  0x01750468-0x0175046b
Note: 0x01750568-0x01750568 overlaps memory 0x01750560-0x01750578 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]

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

DUPLICATE ERROR COUNTS:

SUPPRESSIONS USED:

ERRORS FOUND:
      1 unique,     1 total unaddressable access(es)
      0 unique,     0 total uninitialized access(es)
      1 unique,     1 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:
      1 potential error(s) (suspected false positives)
         (details: C:\AppData\Roaming\Dr. Memory\DrMemory-CPPTests.exe.14664.000\potential_errors.txt)
      3 unique,     9 total,    154 byte(s) of still-reachable allocation(s)
         (re-run with "-show_reachable" for details)
Details: C:\AppData\Roaming\Dr. Memory\DrMemory-CPPTests.exe.14664.000\results.txt

But I was of the impression that this was all fine as I am not keeping any pointers into the vector after it's resized. What am I not understanding?

NeomerArcana
  • 1,978
  • 3
  • 23
  • 50

1 Answers1

1

when I haven't freed any memory?

Sure you have. You (potentially) free memory right here:

data.resize(sizeof(Type)*2);
ts[i].~Type();
} // closes main

The issue is that when the vector reallocates memory, it will copy the unsigned char elements elsewhere. This doesn't work for the Type object created in that memory because Type is not trivially copyable.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • It's true that the vector memory may have been freed, but it's not clear (to me anyway) that that's the cause of the problem Dr. Memory is reporting. Note that the first error happens in fwrite, presumably accessing the string data. If `"something"` is kept in the string (small string optimization), then it is in the vector memory, and you could have a problem. But if it doesn't fit, then it's in the heap, and _that_ memory didn't get freed. And while `"else"` likely does get the SSO, that's created after the vector is resized, so that should not be the problem. – Adrian McCarthy Jun 22 '20 at 19:08
  • Thanks @eerorika but I havent accessed that freed memory. I am accessing it from the vector, hence my confusion – NeomerArcana Jun 22 '20 at 23:36
  • @NeomerArcana The memory in the vector was freed upon resize. – eerorika Jun 23 '20 at 00:23
  • @eerorika I don't think I'm explaining myself well. When I access the vector, in the loop, I'm accessing it directly right? I'm not accessing it through a pointer that I made previously. So if I'm storing data there in the vector, data of a struct with a string, how is that data being freed, it's still in the vector – NeomerArcana Jun 23 '20 at 03:48