This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug libstdc++/40518] New: data races when calling std::string::erase() on empty string
- From: "jwakely dot gcc at gmail dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: 22 Jun 2009 10:27:25 -0000
- Subject: [Bug libstdc++/40518] New: data races when calling std::string::erase() on empty string
- Reply-to: gcc-bugzilla at gcc dot gnu dot org
from http://etbe.coker.com.au/2009/06/22/valgrindhelgrind-and-stl-string
simplified test case:
#include <pthread.h>
#include <string>
void *do_work(void *)
{
std::string s;
s.erase();
return 0;
}
int main()
{
pthread_t tid[2];
pthread_create(&tid[0], NULL, do_work, NULL);
pthread_create(&tid[1], NULL, do_work, NULL);
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
return 0;
}
Helgrind shows data races, because calling erase() on an empty string calls
_M_set_length_and_sharable() on the shared empty _Rep object, which assigns
zero to the refcount, length, and refdata[0].
Those locations contain zero already, so as long as the writes are atomic then
no values are modified, but it is a race.
$ valgrind --tool=helgrind ./a.out
==17699== Helgrind, a thread error detector.
==17699== Copyright (C) 2007-2008, and GNU GPL'd, by OpenWorks LLP et al.
==17699== Using LibVEX rev 1878, a library for dynamic binary translation.
==17699== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.
==17699== Using valgrind-3.4.0, a dynamic binary instrumentation framework.
==17699== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.
==17699== For more details, rerun with: -v
==17699==
==17699== Thread #3 was created
==17699== at 0x8EDC38: clone (in /lib/libc-2.5.so)
==17699== by 0x4AA960F: pthread_create@* (hg_intercepts.c:214)
==17699== by 0x804867D: main (etbe2.cc:16)
==17699==
==17699== Thread #2 was created
==17699== at 0x8EDC38: clone (in /lib/libc-2.5.so)
==17699== by 0x4AA960F: pthread_create@* (hg_intercepts.c:214)
==17699== by 0x8048657: main (etbe2.cc:15)
==17699==
==17699== Possible data race during read of size 4 at 0x607d688 by thread #3
==17699== at 0x602C162: std::string::erase(unsigned int, unsigned int)
(basic_string.h:607)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699== This conflicts with a previous write of size 4 by thread #2
==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:207)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699==
==17699== Possible data race during read of size 4 at 0x607d688 by thread #3
==17699== at 0x602B915: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:607)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699== This conflicts with a previous write of size 4 by thread #2
==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:207)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699==
==17699== Possible data race during read of size 4 at 0x607d690 by thread #3
==17699== at 0x602B9B8: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.tcc:450)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699== This conflicts with a previous write of size 4 by thread #2
==17699== at 0x602B998: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:201)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699==
==17699== Possible data race during write of size 4 at 0x607d690 by thread #3
==17699== at 0x602B998: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:201)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699== This conflicts with a previous write of size 4 by thread #2
==17699== at 0x602B998: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:201)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699==
==17699== Possible data race during write of size 4 at 0x607d688 by thread #3
==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:207)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699== This conflicts with a previous write of size 4 by thread #2
==17699== at 0x602B99F: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (basic_string.h:207)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699==
==17699== Possible data race during write of size 1 at 0x607d694 by thread #3
==17699== at 0x602B9A1: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (char_traits.h:246)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699== This conflicts with a previous write of size 1 by thread #2
==17699== at 0x602B9A1: std::string::_M_mutate(unsigned int, unsigned int,
unsigned int) (char_traits.h:246)
==17699== by 0x602C187: std::string::erase(unsigned int, unsigned int)
(basic_string.h:1133)
==17699== by 0x80486DF: do_work(void*) (etbe2.cc:8)
==17699== by 0x4AA970B: mythread_wrapper (hg_intercepts.c:194)
==17699== by 0x96745A: start_thread (in /lib/libpthread-2.5.so)
==17699== by 0x8EDC4D: clone (in /lib/libc-2.5.so)
==17699==
==17699== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 8 from 2)
The storage is written to, even if not modified, so this comment may not hold
true:
static _Rep&
_S_empty_rep()
{
// NB: Mild hack to avoid strict-aliasing warnings. Note that
// _S_empty_rep_storage is never modified and the punning should
// be reasonably safe in this case.
void* __p = reinterpret_cast<void*>(&_S_empty_rep_storage);
return *reinterpret_cast<_Rep*>(__p);
}
As expected, the helgrind errors are not present with
--enable-fully-dynamic-string
--
Summary: data races when calling std::string::erase() on empty
string
Product: gcc
Version: 4.3.2
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
AssignedTo: unassigned at gcc dot gnu dot org
ReportedBy: jwakely dot gcc at gmail dot com
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40518