[Bug libstdc++/62313] New: Data race in debug iterators
dvyukov at google dot com
gcc-bugzilla@gcc.gnu.org
Sat Aug 30 16:07:00 GMT 2014
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62313
Bug ID: 62313
Summary: Data race in debug iterators
Product: gcc
Version: 5.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: dvyukov at google dot com
$ g++ -v
gcc version 5.0.0 20140830 (experimental) (GCC)
The program is:
=======
#include <list>
#include <pthread.h>
#include <unistd.h>
std::list<int>::iterator iter, iter2;
void *thread(void *arg)
{
for (int i = 0; i < 1000000; i++) {
std::list<int>::iterator it(iter);
iter2 = it;
}
return 0;
}
int main()
{
std::list<int> li;
li.push_back(0);
iter = li.begin();
pthread_t th;
pthread_create(&th, 0, thread, 0);
for (int i = 0; i < 1000000; i++) {
li.push_back(1);
std::list<int>::iterator it = li.begin();
it++;
li.erase(it);
}
pthread_join(th, 0);
}
=====
Build it as:
$ g++ test.cc -Wall -lpthread -fsanitize=thread -pie -fPIE -D_GLIBCXX_DEBUG -g
-O2
Run the program. Output is:
WARNING: ThreadSanitizer: data race (pid=17058)
Read of size 8 at 0x7faedc637b00 by main thread (mutexes: write M2):
#0 void __gnu_debug::_Safe_sequence<std::__debug::list<int,
std::allocator<int> >
>::_M_invalidate_if<__gnu_debug::_Equal_to<std::__cxx1998::_List_const_iterator<int>
> >(__gnu_debug::_Equal_to<std::__cxx1998::_List_const_iterator<int> >)
/foo/include/c++/5.0.0/debug/safe_sequence.tcc:48 (a.out+0x000000002a61)
#1 std::__debug::list<int, std::allocator<int>
>::_M_erase(std::__cxx1998::_List_iterator<int>)
/foo/include/c++/5.0.0/debug/list:464 (a.out+0x000000001e21)
#2 std::__debug::list<int, std::allocator<int>
>::erase(__gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>,
std::__debug::list<int, std::allocator<int> > >)
/foo/include/c++/5.0.0/debug/list:477 (a.out+0x000000001e21)
#3 main test.cc:27 (a.out+0x000000001e21)
Previous write of size 8 at 0x7faedc637b00 by thread T1:
#0 __gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>,
std::__debug::list<int, std::allocator<int> >
>::operator=(__gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>,
std::__debug::list<int, std::allocator<int> > > const&)
/foo/include/c++/5.0.0/debug/safe_iterator.h:223 (a.out+0x000000002460)
#1 thread(void*) test.cc:11 (a.out+0x000000002460)
Location is global 'iter2' of size 40 at 0x7faedc637ae0
(a.out+0x000000203b00)
Mutex M2 (0x7faedb100698) created at:
#0 pthread_mutex_lock
../../.././libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:2686
(libtsan.so.0+0x0000000368a6)
#1 __gthread_mutex_lock
/ssd/src/gcc_trunk/x86_64-unknown-linux-gnu/libstdc++-v3/include/x86_64-unknown-linux-gnu/bits/gthr-default.h:748
(libstdc++.so.6+0x0000000b284b)
#2 __gnu_cxx::__mutex::lock()
/ssd/src/gcc_trunk/x86_64-unknown-linux-gnu/libstdc++-v3/include/ext/concurrence.h:152
(libstdc++.so.6+0x0000000b284b)
#3 __gnu_cxx::__scoped_lock::__scoped_lock(__gnu_cxx::__mutex&)
/ssd/src/gcc_trunk/x86_64-unknown-linux-gnu/libstdc++-v3/include/ext/concurrence.h:244
(libstdc++.so.6+0x0000000b284b)
#4
__gnu_debug::_Safe_sequence_base::_M_attach(__gnu_debug::_Safe_iterator_base*,
bool) ../../../.././libstdc++-v3/src/c++11/debug.cc:268
(libstdc++.so.6+0x0000000b284b)
#5 __libc_start_main <null>:0 (libc.so.6+0x000000021ec4)
Thread T1 (tid=17060, running) created by main thread at:
#0 pthread_create ../../.././libsanitizer/tsan/tsan_interceptors.cc:853
(libtsan.so.0+0x000000026eb4)
#1 main test.cc:22 (a.out+0x000000001aad)
As far as I see the safe iterator first attaches itself to the sequence in
_Safe_iterator_base ctor:
_Safe_iterator(const _Safe_iterator& __x)
: _Safe_iterator_base(__x, _M_constant()), _M_current(__x._M_current)
After that point it's accessible from other threads (in particular that
invalidate other iterators).
Only then it sets _M_current.
But _M_current is already read from another thread that invalidates a different
iterator.
There can be a similar race in dtor. They usually come together.
It can make sense to run some extensive test suite (if one exists) for debug
iterators with -fsanitize=thread -O{0,1,2}.
More information about the Gcc-bugs
mailing list