Discussion:
[Bug c++/60371] New: std::vector::emplace_back
dilyan.palauzov at aegee dot org
2014-02-28 21:34:53 UTC
Permalink
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60371

Bug ID: 60371
Summary: std::vector::emplace_back
Product: gcc
Version: 4.8.3
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: dilyan.palauzov at aegee dot org

I use gcc 4.8.3 20140220 (prerelease), with libc 2.17 on a x86_64 bit system.

Compiling:
#include <vector>
#include <string.h>
#include <stdlib.h>

struct z {
char* var;
z (const char* str) { var = strdup (str); }
~z () { free (var); }
};

std::vector<z> y;

int main () {
y.emplace_back ("a");
y.emplace_back ("b");
}

with
gcc -O0 -ggdb3 -std=c++11 vector_emplace_back.cpp -o vector_emplace_back
-lstdc++
I get the attached binary file.

Running valgrind revision 13842 on the resulting file with

valgrind --tool=memcheck --track-origins=yes ./vector_emplace_back

results the output

== Memcheck, a memory error detector
== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
== Command: ./vector_emplace_back
==
== Invalid free() / delete / delete[] / realloc()
== at 0x4C2B10C: free (in
/usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
== by 0x400B1C: z::~z() (vector_emplace_back.cpp:8)
== by 0x401495: void std::_Destroy<z>(z*) (stl_construct.h:93)
== by 0x401397: void std::_Destroy_aux<false>::__destroy<z*>(z*, z*)
(stl_construct.h:103)
== by 0x4012C5: void std::_Destroy<z*>(z*, z*) (stl_construct.h:126)
== by 0x401103: void std::_Destroy<z*, z>(z*, z*, std::allocator<z>&)
(stl_construct.h:151)
== by 0x4015DC: std::vector<z, std::allocator<z> >::~vector()
(stl_vector.h:415)
== by 0x53871E0: __run_exit_handlers (in /lib64/libc-2.17.so)
== by 0x5387264: exit (in /lib64/libc-2.17.so)
== by 0x5370A2B: (below main) (in /lib64/libc-2.17.so)
== Address 0x59fc090 is 0 bytes inside a block of size 2 free'd
== at 0x4C2B10C: free (in
/usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
== by 0x400B1C: z::~z() (vector_emplace_back.cpp:8)
== by 0x401495: void std::_Destroy<z>(z*) (stl_construct.h:93)
== by 0x401397: void std::_Destroy_aux<false>::__destroy<z*>(z*, z*)
(stl_construct.h:103)
== by 0x4012C5: void std::_Destroy<z*>(z*, z*) (stl_construct.h:126)
== by 0x401103: void std::_Destroy<z*, z>(z*, z*, std::allocator<z>&)
(stl_construct.h:151)
== by 0x400D70: void std::vector<z, std::allocator<z>
::_M_emplace_back_aux<char const (&) [2]>(char const (&) [2]) (vector.tcc:428)
== by 0x400BC6: void std::vector<z, std::allocator<z> >::emplace_back<char
const (&) [2]>(char const (&) [2]) (vector.tcc:101)
== by 0x400A6E: main (vector_emplace_back.cpp:15)
==
==
== HEAP SUMMARY:
== in use at exit: 0 bytes in 0 blocks
== total heap usage: 4 allocs, 5 frees, 28 bytes allocated
==
== All heap blocks were freed -- no leaks are possible
==
== For counts of detected and suppressed errors, rerun with: -v
== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 1 from 1)

There shall not be "double free()".

Inserting y.clear(); between the two y.emplace_back() does not lead to any
warnings from valgrind for the whole resulting programme.

std::list does not have this problem, as expected.
dilyan.palauzov at aegee dot org
2014-02-28 21:35:31 UTC
Permalink
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60371

Дилян Палаузов <dilyan.palauzov at aegee dot org> changed:

What |Removed |Added
----------------------------------------------------------------------------
CC| |dilyan.palauzov at aegee dot org

--- Comment #1 from Дилян Палаузов <dilyan.palauzov at aegee dot org> ---
Created attachment 32236
--> http://gcc.gnu.org/bugzilla/attachment.cgi?id=32236&action=edit
Resulting binary on my system
pinskia at gcc dot gnu.org
2014-02-28 21:55:11 UTC
Permalink
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60371

--- Comment #2 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
This sounds like there is a copy constructor happening and you don't have a
copy constructor defined so it is a direct assignment which means you will get
a double free.
dilyan.palauzov at aegee dot org
2014-02-28 22:07:23 UTC
Permalink
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60371

--- Comment #3 from Дилян Палаузов <dilyan.palauzov at aegee dot org> ---
Indeed, adding

z (const z& x) { var = strdup (x.var); }

solves the problem. However, I don't understand how that "y.clear();" between
the y.emplace_back() in the original program avoids the double free.
dilyan.palauzov at aegee dot org
2014-02-28 22:23:26 UTC
Permalink
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60371

Дилян Палаузов <dilyan.palauzov at aegee dot org> changed:

What |Removed |Added
----------------------------------------------------------------------------
Status|UNCONFIRMED |RESOLVED
Resolution|--- |INVALID

--- Comment #4 from Дилян Палаузов <dilyan.palauzov at aegee dot org> ---
I have to put here some comment, despite the comment I posted on the bug report
some minutes ago. Otherwise the system does not permit me to change the status
from UNCONFIRMED to RESOLVED/INVALID.
redi at gcc dot gnu.org
2014-03-02 16:02:22 UTC
Permalink
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60371

--- Comment #5 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Дилян Палаузов from comment #3)
Post by dilyan.palauzov at aegee dot org
Indeed, adding
z (const z& x) { var = strdup (x.var); }
solves the problem. However, I don't understand how that "y.clear();"
between the y.emplace_back() in the original program avoids the double free.
In the original program the vector is resized on the second insertion, so the
existing element must be copied to the new storage (which results in a shallow
copy of the malloc'd memory, and leads to a double free).

When you clear the vector it doesn't need to be resized, so no element is
copied, so no shallow copy.

Loading...