Bug 210452

Summary: valgrind turns up binary compatibility issue with libstdc++
Product: [Fedora] Fedora Reporter: Stefan Ring <stefanrin>
Component: gcc4Assignee: Jakub Jelinek <jakub>
Status: CLOSED RAWHIDE QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: 5CC: bkoz
Target Milestone: ---Keywords: Reopened
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: 4.1.1-31 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2006-11-05 13:03:38 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Stefan Ring 2006-10-12 08:26:21 UTC
Description of problem:

I use g++ 3.4.6 (vanilla GNU version) for everything on FC5 x86_64 because of
the extremely slow linking process with gcc4 (you may remember my other bugzilla
entry on this issue). Yesterday I started chasing down a problem with valgrind
when I stumbled across something unrelated.

When running a simple C++ program using boost::regex, built with g++ 3.4.6,
valgrind complains:

==27465== Conditional jump or move depends on uninitialised value(s)
==27465==    at 0x387708031D: std::collate<char>::do_transform(char const*, char
const*) const (in /usr/lib64/libstdc++.so.6.0.8)
==27465==    by 0x4A8C78A:
boost::re_detail::cpp_regex_traits_implementation<char>::transform(char const*,
char const*) const (in /usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27465==    by 0x4A8C95D: unsigned
boost::re_detail::find_sort_syntax<boost::re_detail::cpp_regex_traits_implementation<char>,
char>(boost::re_detail::cpp_regex_traits_implementation<char> const*, char*) (in
/usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27465==    by 0x4A90681:
boost::re_detail::cpp_regex_traits_implementation<char>::init() (in
/usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27465==    by 0x4A933F0:
boost::object_cache<boost::re_detail::cpp_regex_traits_base<char>,
boost::re_detail::cpp_regex_traits_implementation<char>
>::do_get(boost::re_detail::cpp_regex_traits_base<char> const&, unsigned long)
(in /usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27465==    by 0x4A93B2E:
boost::object_cache<boost::re_detail::cpp_regex_traits_base<char>,
boost::re_detail::cpp_regex_traits_implementation<char>
>::get(boost::re_detail::cpp_regex_traits_base<char> const&, unsigned long) (in
/usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27465==    by 0x4A94D08: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::do_assign(char const*, char const*, unsigned)
(in /usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27465==    by 0x400BB2: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::assign(char const*, char const*, unsigned)
(in /home/sr/val/a.out)
==27465==    by 0x400B54: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::assign(char const*, unsigned) (in
/home/sr/val/a.out)
==27465==    by 0x4009BD: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::basic_regex(char const*, unsigned) (in
/home/sr/val/a.out)
==27465==    by 0x400942: main (in /home/sr/val/a.out)

The current valgrind generates even more useful output:

==27526== Conditional jump or move depends on uninitialised value(s)
==27526==    at 0x387708031D: std::collate<char>::do_transform(char const*, char
const*) const (basic_string.h:232)
==27526==    by 0x4A8D78A:
boost::re_detail::cpp_regex_traits_implementation<char>::transform(char const*,
char const*) const (in /usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27526==    by 0x4A8D95D: unsigned
boost::re_detail::find_sort_syntax<boost::re_detail::cpp_regex_traits_implementation<char>,
char>(boost::re_detail::cpp_regex_traits_implementation<char> const*, char*) (in
/usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27526==    by 0x4A91681:
boost::re_detail::cpp_regex_traits_implementation<char>::init() (in
/usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27526==    by 0x4A943F0:
boost::object_cache<boost::re_detail::cpp_regex_traits_base<char>,
boost::re_detail::cpp_regex_traits_implementation<char>
>::do_get(boost::re_detail::cpp_regex_traits_base<char> const&, unsigned long)
(in /usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27526==    by 0x4A94B2E:
boost::object_cache<boost::re_detail::cpp_regex_traits_base<char>,
boost::re_detail::cpp_regex_traits_implementation<char>
>::get(boost::re_detail::cpp_regex_traits_base<char> const&, unsigned long) (in
/usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27526==    by 0x4A95D08: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::do_assign(char const*, char const*, unsigned)
(in /usr/lib64/libboost_regex-gcc-mt-1_33_1.so.1.33.1)
==27526==    by 0x400BB2: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::assign(char const*, char const*, unsigned)
(in /home/sr/val/a.out)
==27526==    by 0x400B54: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::assign(char const*, unsigned) (in
/home/sr/val/a.out)
==27526==    by 0x4009BD: boost::basic_regex<char, boost::regex_traits<char,
boost::cpp_regex_traits<char> > >::basic_regex(char const*, unsigned) (in
/home/sr/val/a.out)
==27526==    by 0x400942: main (in /home/sr/val/a.out)

valgrind seems to think that the reference count for some std::string is
uninitialized. I hate to say it, but valgrind is usually right...

Version-Release number of selected component (if applicable):

FC5 current as of today (libstdc++-4.1.1-1.fc5)


How reproducible:

Build gcc 3.4.6 like this:

get *-core-* and *-g++-*, extract, ...
../configure --prefix=/tmp/gcc346 --enable-__cxa_atexit
make bootstrap
make install

/tmp/gcc346/bin/g++ e.cpp -lboost_regex
valgrind ./a.out

The test program e.cpp looks like this:

#include <boost/regex.hpp>

int main()
{
    boost::regex
iso_regex("(\\d{4})-(\\d{2})-(\\d{2})\\s*(\\d{2}):(\\d{2}):(\\d{2})");
    return 0;
}


Actual results:

valgrind complains


Expected results:

no complaints from valgrind

Additional info:

The problem turned up with my custom-compiled boost (compiled with gcc 3.4.6 as
well) but is exactly the same with the one from Fedora (it may be on extras). So
it doesn't matter which compiler was used to build boost, apparently.

The problem disappears when I do LD_LIBRARY_PATH=/tmp/gcc346/lib64. That's why I
labeled it as a binary compatibility issue.

Comment 1 Jakub Jelinek 2006-10-12 13:30:47 UTC
You have way too many things built yourself, this is something you should debug
on your own, I couldn't reproduce it myself using RHEL4 gcc to compile the
testcase.

Comment 2 Stefan Ring 2006-10-13 08:22:49 UTC
I've found a better way to reproduce it. While the valgrind stacktrace looks a
bit different, it seems to be the same problem.

To reproduce, you need a RHEL system and a Fedora 5. I used CentOS but it should
not matter.

I compiled the test program on a freshly installed CentOS 4.3 (RHEL4 Update 3)
like before: g++ e.cpp -lboost_regex
Interestingly, boost is linked in statically.

Now when I use valgrind on it on the CentOS box, nothing shows up.

Then I take the executable and transfer it to the Fedora 5 box where valgrind
will show this:

==2458== Conditional jump or move depends on uninitialised value(s)
==2458==    at 0x40CB97: boost::re_detail::c_traits_base::do_update_collate()
(in /osiris32/home/sr/a.out-centos43)
==2458==    by 0x40CC76: boost::c_regex_traits<char>::update() (in
/osiris32/home/sr/a.out-centos43)
==2458==    by 0x407D60: boost::reg_expression<char, boost::regex_traits<char>,
std::allocator<char> >::set_expression(char const*, char const*, unsigned) (in
/osiris32/home/sr/a.out-centos43)
==2458==    by 0x409B19: boost::reg_expression<char, boost::regex_traits<char>,
std::allocator<char> >::reg_expression(char const*, unsigned,
std::allocator<char> const&) (in /osiris32/home/sr/a.out-centos43)
==2458==    by 0x402A6C: boost::basic_regex<char, boost::regex_traits<char>,
std::allocator<char> >::basic_regex(char const*, unsigned, std::allocator<char>
const&) (in /osiris32/home/sr/a.out-centos43)
==2458==    by 0x4029B9: main (in /osiris32/home/sr/a.out-centos43)
==2458== 
==2458== Conditional jump or move depends on uninitialised value(s)
==2458==    at 0x40CBC7: boost::re_detail::c_traits_base::do_update_collate()
(in /osiris32/home/sr/a.out-centos43)
==2458==    by 0x40CC76: boost::c_regex_traits<char>::update() (in
/osiris32/home/sr/a.out-centos43)
==2458==    by 0x407D60: boost::reg_expression<char, boost::regex_traits<char>,
std::allocator<char> >::set_expression(char const*, char const*, unsigned) (in
/osiris32/home/sr/a.out-centos43)
==2458==    by 0x409B19: boost::reg_expression<char, boost::regex_traits<char>,
std::allocator<char> >::reg_expression(char const*, unsigned,
std::allocator<char> const&) (in /osiris32/home/sr/a.out-centos43)
==2458==    by 0x402A6C: boost::basic_regex<char, boost::regex_traits<char>,
std::allocator<char> >::basic_regex(char const*, unsigned, std::allocator<char>
const&) (in /osiris32/home/sr/a.out-centos43)
==2458==    by 0x4029B9: main (in /osiris32/home/sr/a.out-centos43)

I also tried "yum update"-ing the CentOS box (this includes gcc 3.4.6 instead of
3.4.5) but everything remains the same.

Comment 3 Jakub Jelinek 2006-10-13 09:52:32 UTC
Sure, I can reproduce this, but that in no way can be a binary compatibility
issue.  The problem is in the libboost_regex.a linked statically into the
executable.

Comment 4 Stefan Ring 2006-10-13 10:12:27 UTC
I don't agree with you on this. My understanding of binary compatibility was
that when a shared library minor version number is increased (libstdc++.so 6.0.3
-> 6.0.8 in this case), running binaries linked against the older version should
work just fine when at run-time only the newer version is available.

Comment 5 Jakub Jelinek 2006-10-13 12:27:20 UTC
Sorry, I think you are right.
The relevant change is
http://gcc.gnu.org/ml/libstdc++/2004-10/msg00394.html
and the problem is that in this case _S_create isn't inlined (i.e. a libstdc++.so
routine is used), while _S_construct is inlined:
#2  0x0000003ca2c9cc41 in std::string::_Rep::_S_create () from
/usr/lib64/libstdc++.so.6
#3  0x00000000004058f5 in std::string::_S_construct<char const*> ()
#4  0x000000000040596a in std::basic_string<char, std::char_traits<char>,
std::allocator<char> >::basic_string<char const*> ()
#5  0x000000000040ca04 in boost::re_detail::c_traits_base::do_update_collate ()

Which means this is GCC 3.4.6 _S_construct (which doesn't set _M_refcount)
calling GCC 4.1.1 _S_create (which doesn't set _M_refcount either).
I think we need something like:

--- libstdc++-v3/include/bits/basic_string.tcc  2006-10-05 00:28:47.000000000
+0200
+++ libstdc++-v3/include/bits/basic_string.tcc  2006-10-13 14:24:18.000000000
+0200
@@ -588,6 +588,14 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
       void* __place = _Raw_bytes_alloc(__alloc).allocate(__size);
       _Rep *__p = new (__place) _Rep;
       __p->_M_capacity = __capacity;
+      // ABI compatibility - 3.4.x set in _S_create both
+      // _M_refcount and _M_length.  All callers of _S_create
+      // in basic_string.tcc then set just _M_length.
+      // In 4.0.x and later both _M_refcount and _M_length
+      // are initialized in the callers, unfortunately we can
+      // have 3.4.x compiled code with _S_create callers inlined
+      // calling 4.0.x+ _S_create.
+      __p->_M_set_sharable();
       return __p;
     }



Comment 6 Stefan Ring 2006-10-16 15:43:05 UTC
Thanks! I have installed the patch at our development system, and everything
seems fine.

Comment 7 Jakub Jelinek 2006-11-05 13:03:38 UTC
Fixed in gcc-4.1.1-32 in rawhide.