Bug 49021 - [4.6 regression] BOOST_FOREACH over vector segfaults at runtime
Summary: [4.6 regression] BOOST_FOREACH over vector segfaults at runtime
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.6.1
: P3 normal
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-05-17 12:36 UTC by Mikael Pettersson
Modified: 2011-05-23 09:04 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2011-05-17 17:24:37


Attachments
test case, preprocessed on x86_64-linux (104.77 KB, text/plain)
2011-05-17 12:37 UTC, Mikael Pettersson
Details
test case, non-preprocessed (310 bytes, text/plain)
2011-05-17 12:38 UTC, Mikael Pettersson
Details
reduced by delta (7.48 KB, text/plain)
2011-05-20 13:44 UTC, Jonathan Wakely
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Mikael Pettersson 2011-05-17 12:36:54 UTC
The following test case, which I'll also attach, segfaults at runtime when compiled by gcc-4.6-20110513 on x86_64-linux or i686-linux.  When compiled by gcc-4.5-20110512 it works as expected.

> cat comm-bug-test.cc 
#include <boost/foreach.hpp>
#include <vector>
#include <stdio.h>

std::vector<int> v;

std::vector<int> x;

const inline std::vector<int *>
getv()
{
    const size_t num_accesses(x.size());
    std::vector<int *> ret(num_accesses);
    for (size_t i = 0; i < num_accesses; i++)
        ret[i] = &v[x[i]];
    return ret;
}

int
main(int argc, char **argv)
{
    x.push_back(0);
    x.push_back(1);
    x.push_back(2);
    x.push_back(3);
    x.push_back(4);
    x.push_back(5);

    v.push_back(11);
    v.push_back(12);
    v.push_back(13);
    v.push_back(14);
    v.push_back(15);
    v.push_back(16);

    BOOST_FOREACH(int *w, getv())
        printf("%d\n", *w);

    return 0;
}
> /mnt/misc/install45/bin/g++ -O2 -Wall comm-bug-test.cc; ./a.out
11
12
13
14
15
16
> /mnt/misc/install46/bin/g++ -O2 -Wall comm-bug-test.cc; ./a.out
Segmentation fault
> /mnt/misc/install46/bin/g++ -v
Using built-in specs.
COLLECT_GCC=/mnt/misc/install46/bin/g++
COLLECT_LTO_WRAPPER=/mnt/misc/install46/libexec/gcc/x86_64-unknown-linux-gnu/4.6.1/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: /mnt/misc/gcc-4.6-20110513/configure --prefix=/mnt/misc/install46 --with-gmp=/home/mikpe/pkgs/linux-x86_64/gmp-4.3.2 --with-mpfr=/home/mikpe/pkgs/linux-x86_64/mpfr-2.4.2 --with-mpc=/home/mikpe/pkgs/linux-x86_64/mpc-0.8.2 --disable-plugin --disable-lto --disable-nls --disable-libmudflap --enable-threads=posix --enable-checking=release --enable-languages=c,c++,objc,obj-c++,fortran,java,ada
Thread model: posix
gcc version 4.6.1 20110513 (prerelease) (GCC)

Initially found by a colleague when testing our code on Fedora 15 with Red Hat's gcc-4.6.0 (20110428) and boost 1.46.  I then confirmed the bug on Fedora 13 with a self-compiled gcc-4.6-20110513 and boost-1.41.  -m32 or -m64 makes no difference.
Comment 1 Mikael Pettersson 2011-05-17 12:37:52 UTC
Created attachment 24260 [details]
test case, preprocessed on x86_64-linux
Comment 2 Mikael Pettersson 2011-05-17 12:38:38 UTC
Created attachment 24261 [details]
test case, non-preprocessed
Comment 3 Jonathan Wakely 2011-05-17 13:52:40 UTC
The temporary returned by getv() seems to be destroyed too early, before the printf that uses it
Comment 4 Jonathan Wakely 2011-05-17 17:24:37 UTC
slightly reduced, but boost/foreach.hpp still includes thousands of lines

in this version vector<T> zeros its data member on destruction, so using it after destruction is obvious

#include <boost/foreach.hpp>

template<typename T>
struct vector
{
    typedef const T* const_iterator;

    ~vector() { data = T(); }

    T data;

    const_iterator begin() const { return &data; }
    const_iterator end() const { return &data + 1; }
};

vector<int> v = { 1 };

const vector<int *>
getv()
{
    vector<int *> ret = { &v.data };
    return ret;
}

int
main(int argc, char **argv)
{
    BOOST_FOREACH(int *w, getv())
        __builtin_printf("%d\n", *w);

    return 0;
}
Comment 5 Jonathan Wakely 2011-05-17 17:28:44 UTC
slightly more reduced

#include <boost/foreach.hpp>

struct vector
{
    typedef int* const* const_iterator;

    ~vector() { data = 0; }

    int* data;

    const_iterator begin() const { return &data; }
    const_iterator end() const { return &data + 1; }
};

int v = 1;

const vector
getv()
{
    vector ret = { &v };
    return ret;
}

int
main(int argc, char **argv)
{
    BOOST_FOREACH(int *w, getv())
        __builtin_printf("%d\n", *w);

    return 0;
}
Comment 6 Mikael Pettersson 2011-05-19 23:37:54 UTC
The regression started with r170601:

Author: jason
Date: Tue Mar  1 22:44:35 2011
New Revision: 170601

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=170601
Log:
	PR c++/47851
	* call.c (standard_conversion): Provide requested cv-quals on
	class rvalue conversion.
Comment 7 Jonathan Wakely 2011-05-20 00:11:19 UTC
well if the fix for PR 47851 made G++ correct then this could be a bug in BOOST_FOREACH, but it's hard to know because trying to reduce any MPL code is pretty tedious!

Jason, any ideas?  do we need to reduce it with delta?
Comment 8 Mikael Pettersson 2011-05-20 08:27:39 UTC
I'm trying to reduce the preprocessed test case, but since I'm not a C++ person this might take some time.
Comment 9 Mikael Pettersson 2011-05-20 09:05:31 UTC
(In reply to comment #8)
> I'm trying to reduce the preprocessed test case, but since I'm not a C++ person
> this might take some time.

Scratch that, the preprocessed code is way too weird for me.
Comment 10 Jonathan Wakely 2011-05-20 09:48:42 UTC
(In reply to comment #9)
> (In reply to comment #8)
> > I'm trying to reduce the preprocessed test case, but since I'm not a C++ person
> > this might take some time.
> 
> Scratch that, the preprocessed code is way too weird for me.

welcome to Boost MPL!  ;)

there are huge chunks that can be removed (iostreams and locales, reverse_iterators) but reducing it manually is not easy.

See http://gcc.gnu.org/wiki/A_guide_to_testcase_reduction for a tool to do it automatically
Comment 11 Jonathan Wakely 2011-05-20 13:44:10 UTC
Created attachment 24299 [details]
reduced by delta
Comment 12 Jason Merrill 2011-05-20 21:25:50 UTC
This is a bug in BOOST_FOREACH_COMPILE_TIME_CONST_RVALUE_DETECTION.

The GCC bugfix meant that where previously

true ? boost::foreach_detail_::make_probe(getv()) : (getv())

had the type "vector", now it has type "const vector".

5.16/3 says:

Otherwise (i.e., if E1 or E2 has a nonclass type, or if they both have class types but the underlying classes are not either the same or one a base class of the other): E1 can be converted to match E2 if E1 can be implicitly converted to the type that expression E2 would have if E2 were converted to a prvalue (or the type it has, if E2 is a prvalue).

"const vector" cannot be converted to make_probe<vector>, but make_probe<vector> has an operator vector&, so it can be converted to const vector, so the type of the conditional expression is const vector.

Here's a reduced example:

template <class T>
void f (T&, int);

template <class T>
void f (T const&, ...);

struct A { };
struct B { operator A& () const; };

const A g();

int main()
{ 
  f( (true ? B() : g()), 0);
}
Comment 13 Michel Morin 2011-05-23 09:04:14 UTC
(In reply to comment #12)
> This is a bug in BOOST_FOREACH_COMPILE_TIME_CONST_RVALUE_DETECTION.

This was already fixed in boost/trunk, 
and the fix will be released in Boost.1.47. 

For details, please see the following ticket:
[Foreach] Compile-time const rvalue detection fails with gcc 4.6
https://svn.boost.org/trac/boost/ticket/5279