C++ segfault under gcc+mingw but not gcc+linux - c++

I have a sefault in a big program under Mingw but not under Linux. I managed to reduce it to the following code:
#include <iostream>
#include <string>
#include <map>
using namespace std;
const std::string& getConfiguration(const std::string& key, std::map<std::string, std::string> configuration)
{
map<string, string>::const_iterator it = configuration.find(key);
return it->second;
}
int main() {
map<string, string> testmap;
testmap["key"] = "value";
map<string, string>::const_iterator it;
it = testmap.find("key");
const string& value = it->second;
cout << value << endl;
cout << "ok!" << endl;
const string& valuebis = getConfiguration("key", testmap);
cout << valuebis << endl;
cout << "Mingw segfaults before here" << endl;
return 0;
}
I compile it with mxe, with the following options
/path/to/mxe/usr/bin/i686-w64-mingw32.static-g++ -ggdb3 -O0 -o test.exe test.cpp -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32
(but I get the same result with i686-pc-mingw32.static) and I get the following error in gdb:
Starting program: Z:\test-conv.exe
[New Thread 17328.0x43b4]
value
ok!
Program received signal SIGSEGV, Segmentation fault.
std::operator<< <char, std::char_traits<char>, std::allocator<char> > (__os=warning: RTTI symbol not found for class 'std::ostream'
..., __str=<error reading variable: Cannot access memory at address 0xfeeefee2>)
at /home/eroux/softs/mxe-64/tmp-gcc-i686-w64-mingw32.static/gcc-4.9.1.build/i686-w64-mingw32.static/libstdc++-v3/include/bits/basic_string.h:2777
2777 /home/eroux/softs/mxe-64/tmp-gcc-i686-w64-mingw32.static/gcc-4.9.1.build/i686-w64-mingw32.static/libstdc++-v3/include/bits/basic_string.h: No such file or directory.
(gdb) bt
#0 std::operator<< <char, std::char_traits<char>, std::allocator<char> > (__os=warning: RTTI symbol not found for class 'std::ostream'
..., __str=<error reading variable: Cannot access memory at address 0xfeeefee2>)
at /home/eroux/softs/mxe-64/tmp-gcc-i686-w64-mingw32.static/gcc-4.9.1.build/i686-w64-mingw32.static/libstdc++-v3/include/bits/basic_string.h:2777
#1 0x0040181a in main () at ConventionTest.cpp:22
My guess is that somehow the iterator in getConfiguation gets freed by mingw but not by usual linux gcc...
But as it works fine under Linux and as gcc issues no warning at all (even with -Wextra), I wonder if there is a bad practice somewhere (I'm no C++ expert) or if it's a bug in mingw optimizations...
Thank you,

getConfiguration returns a reference to a local object (and so invoke UB), pass configuration as const reference (or return a copy of the string).

It's because of undefined behavior. The reason is that in the getConfiguration function you pass the map by value, and then you return a reference to an entry in this map, which will be destructed when the function returns.

Related

Boost property_tree error: conversion of data to type "j" failed when getting an element in a .ini file

I have a .ini file contains below data
[SYSTEM]
num_of_vps = 1
And I have this code to read an element in .ini file. (uint defined as typedef unsigned int uint)
boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini(iniFilePath, pt);
hwCount = pt.get<uint>("SYSTEM.num_of_vps");
I created a from files including above code and called it in a wrapper function in main.cc file. Then I am getting below error
terminate called after throwing an instance of 'boost_1_68_0::exception_detail::clone_impl<boost_1_68_0::exception_detail::error_info_injector<boost_1_68_0::property_tree::ptree_bad_data> >'
what(): conversion of data to type "j" failed
stack trace
#12 0x00002aaab613fcd5 in abort () from /lib64/libc.so.6
#13 0x00002aaab9b29315 in __gnu_cxx::__verbose_terminate_handler () at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/vterminate.cc:95
#14 0x00002aaab9a9e8f6 in __cxxabiv1::__terminate (handler=<optimized out>) at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:47
#15 0x00002aaab9a9e941 in std::terminate () at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:57
#16 0x00002aaab9a9ea74 in __cxxabiv1::__cxa_throw (obj=<optimized out>, tinfo=0x2aaab9e1ff60 <typeinfo for boost_1_68_0::exception_detail::clone_impl<boost_1_68_0::exception_detail::error_info_injector<boost_1_68_0::property_tree::ptree_bad_data> >>, dest=0x2aaab99bef18 <boost_1_68_0::exception_detail::clone_impl<boost_1_68_0::exception_detail::error_info_injector<boost_1_68_0::property_tree::ptree_bad_data> >::~clone_impl()>) at ../../../../src/gcc-7.3.0/libstdc++-v3/libsupc++/eh_throw.cc:93
#17 0x00002aaab99bec82 in boost_1_68_0::throw_exception<boost_1_68_0::exception_detail::error_info_injector<boost_1_68_0::property_tree::ptree_bad_data> > (e=...) at throw_exception.hpp:72
#18 0x00002aaab99be576 in boost_1_68_0::exception_detail::throw_exception_<boost_1_68_0::property_tree::ptree_bad_data> (x=..., current_function=0x2aaab9b45fc0 <boost_1_68_0::enable_if<boost_1_68_0::property_tree::detail::is_translator<boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int> >, unsigned int>::type boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<unsigned int, boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int> >(boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int>) const::__PRETTY_FUNCTION__> "typename boost_1_68_0::enable_if<boost_1_68_0::property_tree::detail::is_translator<Translator>, Type>::type boost_1_68_0::property_tree::basic_ptree<Key, Data, KeyCompare>::get_value(T"..., file=0x2aaab9b45830 "property_tree/detail/ptree_implementation.hpp", line=675) at throw_exception.hpp:89
#19 0x00002aaab99be01e in boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<unsigned int, boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int> > (this=0xbc86c8, tr=...) at property_tree/detail/ptree_implementation.hpp:673
#20 0x00002aaab99bd6a5 in boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<unsigned int> (this=0xbc86c8) at property_tree/detail/ptree_implementation.hpp:683
#21 0x00002aaab99bc545 in boost_1_68_0::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get<unsigned int> (this=0x7fffffff9470, path=...) at property_tree/detail/ptree_implementation.hpp:754
#22 0x00002aaab99bba83 in MyRT::DUTConfigFile::readIniFile (this=0xbc5d50, iniFilePath=...) at DUTConfigFile.cpp:231
#23 0x00002aaab99ba8d2 in MyRT::DUTConfigFile::DUTConfigFile (this=0xbc5d50, iniFilePath=..., configFilePath=...) at DUTConfigFile.cpp:26
#24 0x00002aaab99c0839 in setupMyConfigs () at SimXLInterface.cpp:83
#31 0x0000000000408847 in main ()
I tried gdb and it is throwing an exception when converting string to uint using istringstream. Below are the two types of the transition
template<class K, class D, class C>
template<class Type> inline
Type basic_ptree<K, D, C>::get_value() const
{
return get_value<Type>(
typename translator_between<data_type, Type>::type());
}
(gdb) p typeid(Type).name()
$2 = 0x2aaab5c33c91 <typeinfo name for unsigned int> "j"
(gdb) p typeid(data_type).name()
could not find typeinfo symbol for 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >'
Below function in basic_ios.h throws a __throw_bad_cast() exception (here this->_M_num_get = NULL)
const __num_get_type& __ng = __check_facet(this->_M_num_get);
Internal build automation Makefile is addding -D_GLIBCXX_USE_CXX11_ABI=0 to object file creation by default
g++ -I/usr/myboost/boost/boost -Wall -std=c++14 -fopenmp -m64 -msse2 -fPIC \
-D_GLIBCXX_USE_CXX11_ABI=0 -g -o *.o -fPIC -c *.cpp
.so building command
g++ -m64 -msse2 -pthread -shared -static-libstdc++ -static-libgcc \
-Wl,-znoexecstack -g -o runtime.so *.o -ldl -lrt -lz -fopenmp -lcrypto
If I change it to read a std::string type and later convert it to uint, it is working without an exception
std::string strHwCount = pt.get<std::string>("SYSTEM.num_of_vps");
hwCount = static_cast<uint>(std::stoul(strHwCount));
I am using boost-1.68 and gcc-7.3. Dose this have to do with the ABI macro that I'm using? Is there a way to resolve this without above workaround
UPDATE
First of all I am migrating project from cmake to a internal custom build automation (some enhanced version of Makefile). In cmake this worked fine. Based on sehe's answer I did some further analysis.
In a small standalone example this error is not reproducible (using both local string and actual .ini file read).
When going through gdb, In file boost/property_tree/stream_translator.hpp, I could find the read value from the file (changed to num_of_vps = 4 in file) in the both string and istringstream.
optional<Type> basic_ptree<K, D, C>
::get_value_optional(Translator tr) const
{
return tr.get_value(data());
}
(gdb) p data()
$1 = "4"
boost_1_68_0::optional<E> get_value(const internal_type &v) {
std::basic_istringstream<Ch, Traits, Alloc> iss(v);
iss.imbue(m_loc);
E e;
customized::extract(iss, e);
....
}
(gdb) p v
$1 = "4"
(gdb) p iss.str()
$2 = "4"
Inside above customized::extract(iss, e) function there is a string to unsigned int conversion using a basic_istream
static void extract(std::basic_istream<Ch, Traits>& s, E& e) {
s >> e;
if(!s.eof()) { s >> std::ws; }
}
Inside this >> operator there is a _M_extract function and inside that __check_facet function throws the exception (by cheking NULL of this->_M_num_get )
basic_istream<_CharT, _Traits>::
_M_extract(_ValueT& __v) {
__try {
const __num_get_type& __ng = __check_facet(this->_M_num_get);
...
inline const _Facet&
__check_facet(const _Facet* __f) {
if (!__f)
__throw_bad_cast();
return *__f;
}
stack trace above scenario
#0 std::istream::_M_extract<unsigned int>(unsigned int&) (__f=0x0) at gcc-7.3.0/objdir/x86_64-centos-linux/libstdc++-v3/include/bits/basic_ios.h:49
#1 std::istream::operator>>(unsigned int&) (this=0x7fffffff9980, __n=#0x7fffffff997c: 0)
#2 boost_1_68_0::property_tree::customize_stream::extract(std::istream&, unsigned int&) (s=..., e=#0x7fffffff997c: 0)
#3 boost_1_68_0::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, unsigned int>::get_value(std::string const&) (this=0x7fffffff9b58, v="4")
I could see the same behavior in the another place that we are using std::stringstream . Variable ss shows empty even with << operator adding strings
std::stringstream ss;
ss << std::setw(8) << std::setfill('0') << std::hex << firmId;
I had a concern about that the ABI flag causing this, but I was able to remove it and the issue is still there. I searched about this kind of istream issues but couldn't find anything useful.
Your input is corrupt. Likely it's using an encoding or code-points that you didn't expect, but don't stand out for a human reader.
Live On Coliru
#include <boost/property_tree/ini_parser.hpp>
#include <iostream>
int main() {
std::istringstream iss(R"([SYSTEM]
num_of_vps = 1)");
boost::property_tree::ptree pt;
read_ini(iss, pt);
uint hwCount = pt.get<uint>("SYSTEM.num_of_vps");
std::cout << "hwCount: " << hwCount << "\n";
}
Prints
hwCount: 1
However, if you change the input to be e.g.
std::istringstream iss(R"([SYSTEM]
num_of_vps = 1)");
(Note that the space after = is now < > 160, \240, U+00A0 NO-BREAK SPACE, ^KNS, ) it prints:
Live On Coliru
Prints
terminate called after throwing an instance of 'boost::wrapexcept<boost::property_tree::ptree_bad_data>'
what(): conversion of data to type "j" failed
With a tweak to the gcc command I was able to run the executable with the .so with all sstream usages. Previously the gcc linking command was
g++ -m64 -msse2 -pthread -shared -static-libstdc++ -static-libgcc \
-Wl,-znoexecstack -g -o runtime.so *.o -ldl -lrt -lz -fopenmp -lcrypto
Removing -static-libstdc++ and dynamically linking libstdc++.so worked
g++ -m64 -msse2 -pthread -shared -static-libgcc -Wl,-znoexecstack -o runtime.so *.o \
-fPIC -ldl -lrt -lz -fopenmp -lcrypto -Wl,-rpath,/depot/gcc-7.3.0/lib64

error in compilation while looping over map

got compilation error while trying to compile simple cpp program
i have tried the following compilation lines
1) gcc hello.cpp -std=gnu++11
2) g++ hello.cpp -std=gnu++11
this is my cpp file
#include <iostream>
#include <map>
#include <string>
using namespace std;
static map<string,int> create_map()
{
map<string,int> mymap = {
{ "alpha", 10 },
{ "beta", 20 },
{ "gamma", 30 } };
return mymap;
}
map<string,int> m= create_map();
int main()
{
cout << "Hello, World!";
for (map<string,int> x: m)
cout << x.first << ": " << x.second << '\n';
return 0;
}
the output of gcc contain a lot of linkage error like
hello.cpp:(.text+0x135): undefined reference to `std::cout'
hello.cpp:(.text+0x13a): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
and g++ output was shorter
hello.cpp: In function 'int main()':
hello.cpp:19:29: error: conversion from 'std::pair<const std::basic_string<char>, int>' to non-scalar type 'std::map<std::basic_string<char>, int>' requested
hello.cpp:20:18: error: 'class std::map<std::basic_string<char>, int>' has no member named 'first'
hello.cpp:20:37: error: 'class std::map<std::basic_string<char>, int>' has no member named 'second'
the code is correct for 99.99%. i don't know really how to compile it properly
There are some very different kind of errors you see.
gcc hello.cpp -std=gnu++11
This tries to compile the code using the GNU c-compiler.
C++ source files are recognized and compiled regarding the c++ syntax, but the linker uses c standard libraries, and the c++ standard libraries aren't automatically linked.
There must be some difference with your source, since I can't reproduce the linker errors you're claiming with this test.
In the other case
g++ hello.cpp -std=gnu++11
your code is compiled using the c++ compiler, and
for (map<string,int> x: m)
is wrong regarding the type for x in the range based loop (which actually is pair<const string,int>).
You should simply change that to
for (const auto& x: m)
to get the code working.
x will be the type of the container element type, that is iterated over from m.
Here's a working online demo.
Replace the loop with:
for (auto const& x: m)
cout << x.first << ": " << x.second << '\n';
Alternatively, you could use:
for (std::pair<string, int> x: m)
or
for (const std::pair<string, int>& x: m)
const because you do not want to change x. & to prevent a copy of the pair into x.
In the range based loop for an std::map, the variable that iterates over the container needs to be std::pair<const Key, T>. This is defined by the value_type for the container's Member types.
Since the type of x is std::pair<const string,int> you need to use instead (after comments):
for (const std::pair<const string,int>& x: m)
const & is added so that redundant pair copies are avoided (in the newest compilers they are probably avoided any way).
for (const auto& x: m)
may alternatively be used!

Unable to run simple std::async and std::future test program. Error: "has initializer but incomplete type." What's going on?

I'm trying my hand at asynchronous function calls in C++ using std::async in accordance with this official cplusplus.com sample code.
Unfortunately, compilation fails. When running mingw32-make, I get the following errors:
main.cpp:37:23: error: variable 'std::future<bool> the_future' has initializer but incomplete type
main.cpp:37:61: error: invalid use of incomplete type 'class std::future<bool>'
I also tried running make through WSL (Windows Subsystem for Linux), which essentially makes Linux bash available on Windows. Doesn't work on there either:
main.o: In function `std::thread::thread<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<bool (*)(int), long> >, bool>::_Async_state_impl(std::thread::_Invoker<std::tuple<bool (*)(int), long> >&&)::{lambda()#1}>(std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<bool (*)(int), long> >, bool>::_Async_state_impl(std::thread::_Invoker<std::tuple<bool (*)(int), long> >&&)::{lambda()#1}&&)':
/usr/include/c++/7/thread:122: undefined reference to `pthread_create'
I've tried the following:
Updating my compiler using this SO answer.
As noted above, attempting to make using two different approaches. Both fail.
Using gcc instead of g++ (just a different compiler).
Here is main.cpp:
#include <iostream>
#include <string>
#include <cstdlib>
#include <future>
// a non-optimized way of checking for prime numbers
bool is_prime (int x)
{
std::cout << "Calculating. Please, wait..." << std::endl;
for (int i=2; i<x; ++i)
{
if(x % i == 0) return false;
}
return true;
}
void interpret_result (bool result)
{
if (result)
{
std::cout << "It is prime!" << std::endl;
}
else
{
std::cout << "It is not prime!" << std::endl;
}
}
int main()
{
long num = 313222313;
// The result of the asynchronous call to is_prime will be stored in the_future
std::future<bool> the_future = std::async (is_prime, num);
std::cout << "Checking whether " << num << " is a prime number!" << std::endl;
// Nothing beyond this line runs until the function completes
bool result = the_future.get();
// Interpret the result
interpret_result (result);
// So the cmd window stays open
system ("pause");
return 0;
}
And here is my makefile (I like to create one for each project just to practice):
COMPILER = g++
COMPILER_FLAGS = -std=c++17 -Wall -g
LINKER_FLAGS =
EXECUTABLE = async
all: main clean
main: main.o
$(COMPILER) $(LINKER_FLAGS) -o $(EXECUTABLE) main.o
main.o: main.cpp
$(COMPILER) $(COMPILER_FLAGS) -c main.cpp
.PHONY: clean
clean:
rm *.o
I'm not sure why this code isn't compiling. I've run through it and cannot identify any errors. mingw updated successfully (I restarted my terminal afterwards).
Any help would be appreciated.
main.o: In function `std::thread::thread<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<bool (*)(int), long> >, bool>::_Async_state_impl(std::thread::_Invoker<std::tuple<bool (*)(int), long> >&&)::{lambda()#1}>(std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<bool (*)(int), long> >, bool>::_Async_state_impl(std::thread::_Invoker<std::tuple<bool (*)(int), long> >&&)::{lambda()#1}&&)':
/usr/include/c++/7/thread:122: undefined reference to `pthread_create'
This can be fixed by linking with pthread library, probably by adding -lpthread to your LINKER_FLAGS.

different output for -fsanitize=address with clang++ vs g++

In the following code I have a problem. When I give it a vector that is still completely empty the code crashes, because vector.size() - 1 can't be negative, hence it wraps around. Because the vector is empty accessing container[0] is invalid.
using namespace std;
template<typename T, typename A>
std::string vec_to_python_list(
const std::string& name,
const vector<T, A>& container
) {
std::stringstream stream(ios::out);
stream << name << " = [" << '\n';
for (typename vector<T, A>::size_type i = 0; i < container.size() - 1; i++)
stream << "\t" << container[i] << ",\n";
if (name.size() > 0)
stream << "\t" << container[container.size() - 1] << "\n";
stream << "]";
return stream.str();
}
My question is about the output that it produces.
If I compile with g++ on Ubuntu-16.04
g++ -Wall -Wextra -std=c++14 -g -fsanitize=address -O0 -o test_kmeans test_kmeans.cpp
I obtain the next useful info:
#0 0x402a56 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > vec_to_python_list<double, std::allocator<double> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<double, std::allocator<double> > const&) /home/hetepeperfan/programming/cpp/kmeans/test_kmeans.cpp:46
#1 0x401f07 in main /home/hetepeperfan/programming/cpp/kmeans/test_kmeans.cpp:66
#2 0x7f393881f82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x401ba8 in _start (/home/hetepeperfan/programming/cpp/kmeans/test_kmeans+0x401ba8)
However if i compile with clang++ (still on Ubuntu-16.04)
clang++ -Wall -Wextra -std=c++14 -g -fsanitize=address -O0 -o test_kmeans test_kmeans.cpp
I get less useful results:
#0 0x4eecae (/home/hetepeperfan/programming/cpp/kmeans/test_kmeans+0x4eecae)
#1 0x4ee056 (/home/hetepeperfan/programming/cpp/kmeans/test_kmeans+0x4ee056)
#2 0x7f6ed883a82f (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x419838 (/home/hetepeperfan/programming/cpp/kmeans/test_kmeans+0x419838)
What am I do'ing wrong so that g++ with -fsanitize=address works and clang++ doesn't produce as useful results, It seems like the debugging symbols aren't added?
EDIT
The debug symbols seem to be present, because with gdb I can easily step through the code and with the --tui gdb option I can see my code, so that isn't the issue.
Install the llvm-symbolizer.
Also set the ASAN_SYMBOLIZER_PATH environment variable to something like
ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer
llvm is looking for an executable named llvm-symbolizer not llvm-symbolizer-3.8, that's why the environment variable must point to llvm-symbolizer a filename that has no version number. If all your symbolizers have version numbers in their file name, make a symbolic link that has no version number in it.

std::map argument with empty brace-initializers for default argument segfaults in GCC

Problem
I got a bug report from user reporting a segfault in library I develop.
The minimal example of the faulty code is:
#include <map>
#include <string>
#include <iostream>
void f(std::map<std::string, std::string> m = {})
{
std::cout << m.size() << "\n";
for (const auto& s: m) {
std::cout << s.first << "->" << s.second <<"\n";
}
}
int main()
{
f();
}
When compiled with GCC (I tested 4.8.2 and 4.7.3) it correctly prints 0 as size of the container, but segfaults inside the loop (which shouldn't be executed at all).
Workarounds
However, I can fix the problem by changing the declaration to:
void f(std::map<std::string, std::string> m = std::map<std::string, std::string>{})
Copying the map works as well:
void f(std::map<std::string, std::string> mx = {})
{
auto m = mx;
std::cout << m.size() << "\n";
for (const auto& s: m) {
std::cout << s.first << "->" << s.second <<"\n";
}
}
Changing the parameter to const std::map<...>& also works.
GCC 4.9.1 works fine.
Clang also compiles and runs the code just fine. (even when using the same libstdc++ as failing gcc 4.8.2)
Working example: http://coliru.stacked-crooked.com/a/eb64a7053f542efd
Question
The map is definitely not in valid state inside the function (details bellow).
It looks like a GCC (or libstdc++) bug, but I want to be sure I'm not making some stupid mistake here.
It's hard to believe such a bug would stay in gcc for at least 2 major version.
So my question is: Is the way of initializing default std::map parameter wrong (and bug in my code) or is it a bug in stdlibc++ (or gcc)?
I'm not looking for workarounds (as I know what to do to make to code work)
When integrated in the application, the offending code executes fine on some computers (even when compiled with gcc 4.8.2) on some doesn't.
Details
I compile it using:
g++-4.8.2 -g -Wall -Wextra -pedantic -std=c++11 /tmp/c.cpp -o /tmp/t
Backtrace from gdb:
#0 std::operator<< <char, std::char_traits<char>, std::allocator<char> > (__os=..., __str=...) at /usr/src/debug/sys-devel/gcc-4.8.2/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:2758
#1 0x0000000000400f36 in f (m=std::map with 0 elements) at /tmp/c.cpp:9
#2 0x0000000000400fe0 in main () at /tmp/c.cpp:15
/tmp/c.cpp:9 is the line with std::cout << ...
ASAN reports:
AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8
This seems like nullptr - 8
valgrind shows:
==28183== Invalid read of size 8
==28183== at 0x4ECC863: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.2/libstdc++.so.6.0.18)
==28183== by 0x400BD5: f(std::map<std::string, std::string, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >) (c.cpp:9)
==28183== by 0x400C7F: main (c.cpp:15)
==28183== Address 0xffffffffffffffe8 is not stack'd, malloc'd or (recently) free'd
Looking at internal state of the map shows that the code really has to fail:
std::map::begin() in libstdc++ returns value of
this->_M_impl._M_header._M_parent
from it's internal representation, std::map::end() returns:
&this->_M_impl._M_header
gdb shows:
(gdb) print m._M_t._M_impl._M_header
$5 = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffd6d8, _M_right = 0x7fffffffd6d8}
(gdb) print &m._M_t._M_impl._M_header
$6 = (std::_Rb_tree_node_base *) 0x7fffffffd6a8
So value of begin() and end() are not the same (begin() is nullptr) as mandated by standard for empty std::map.
Looks like this bug was fixed in 4.8.3/4.9.0, the bug report which has a similar example and also seg-faults says:
The attached minimal testcase has the following function with
default-constructed default argument:
void do_something( foo f = {} )
{ std::cout << "default argument is at " << &f << std::endl;
}
The constructor for foo outputs its address; I got the following
output from a single run:
constructed foo # 0x7ffff10bdb7f
default argument is at 0x7ffff10bdb60
It shows that only 1 foo was constructed, and not at the same address
as that of the default argument. It's been a loooong week, but I can't
see anything wrong with the code. In the real code on which this was
based, a segfault was occurring when running the destructor of a foo
that was move-constructed from the default argument, because the
underlying memory was seemingly uninitialised.
We can see from a live example that 4.9.0 does not demonstrate this problem.
We can see this was intentional functionality from defect report 994 and the subsequent resolution N3217:
This paper presents detailed wording changes relative to the current
C++ Working Draft N3126 to implement brace-initializers for default
arguments for functions, as proposed in N3139 "An Incomplete Language
Feature" by Bjarne Stroustrup, thereby also addressing core issue 994.
This is also covered in the proposal N3139: An Incomplete Language Feature.
Interesting to note that Visual Studio also has a bug with respect to brace-initializers as default arguments which I think is still unresolved.