This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug libstdc++/40518] New: data races when calling std::string::erase() on empty string


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]