How to find and avoid uninitialised primitive members in C++? - c++

There is a common C++ bug with uninitialised primitive members:
#include <iostream>
class A {
public:
int x;
};
int main() {
A a;
std::cout << a.x;
return 0;
}
a.x will be uninitialised. I understand why it happens and want to find a solution for catching such bugs. I checked gcc and cppcheck, they don't report these members.
EDIT
Checked gcc with flags -Wall -Wextra -Werror -pedantic -Wold-style-cast -Wconversion -Wsign-conversion -Wunreachable-code
The first version of gcc that detects a bug is 5.1. g++-4.9 doesn't detect it, clang++-3.6 fails too.

Yes they do… sort of:
main.cpp: In function 'int main()':
main.cpp:10:18: warning: 'a.A::x' is used uninitialized in this function [-Wuninitialized]
std::cout << a.x;
^
0
In the above linked example, I'm using GCC 5.1 trunk with -Wall.
Turn on more GCC warnings and/or upgrade.
Also tested, and found not to warn:
GCC 4.4.7
GCC 4.9.2 (by RiaD)
Clang 3.6.0
Honestly I'm not sure what else you can do. You could create a tool to do this, but then you'd be creating a compiler or static analyser. :)
So, I guess, just hope the people who know how to do that catch up…

This bug is diagnosed by valgrind with the (default) tool memcheck, producing a number of warnings, including:
$ valgrind ./unin
…
==12185== Use of uninitialised value of size 8
==12185== at 0x4F39BC3: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==12185== by 0x4F3AD89: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==12185== by 0x4F3AF8C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==12185== by 0x4F474E9: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==12185== by 0x400763: main (in [censored]/unin)
It should also be found by Clang's address sanitizer. However, I realize you are most interested in a compile-time check. After all, the existing test suites may never execute some of the code, and catching a bug sooner is always better. You could use GCC 5.1 (even if you used it only for this purpose), or you could use a dedicated static analyzer. Fortunately, clang comes with a static analyzer invoked as scan-build (included in at least the Debian/Ubuntu packages):
$ scan-build clang -c unin.cxx
scan-build: Using '/usr/lib/llvm-3.6/bin/clang' for static analysis
unin.cxx:10:3: warning: Function call argument is an uninitialized value
std::cout << a.x;
^~~~~~~~~~~~~~~~
1 warning generated.
scan-build: 1 bug found.

Related

Why does clang MemorySanitizer produce error in fstream open?

I am trying to integrate Memory Sanitizer into my project. And following code raise an use-of-uninitialized-value error:
#include <fstream>
int main () {
std::ifstream ifs;
ifs.open ("test.txt", std::ifstream::in);
char c = ifs.get(); // this line doesn't matter
if (!ifs.good()) {
return -1;
}
ifs.close();
return 0;
}
$ clang -fsanitize=memory -fPIE -pie -fno-omit-frame-pointer -g -O3 main.cpp -lstdc++
$ ./a.out
==43028==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x55986e5d76ae in std::basic_ifstream<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/fstream
#1 0x55986e5d76ae in main /home/promanjuk/learn/sanityzers/main.cpp:5:7
#2 0x7f6389ffdd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#3 0x7f6389ffde3f in __libc_start_main csu/../csu/libc-start.c:392:3
#4 0x55986e551334 in _start (/home/promanjuk/learn/sanityzers/a.out+0x21334) (BuildId: 597886c0634c006622192d0fa4c5ab7f169b7625)
SUMMARY: MemorySanitizer: use-of-uninitialized-value /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/fstream in std::basic_ifstream<char, std::char_traits<char> >::open(char const*, std::_Ios_Openmode)
Exiting
$ clang --version
Ubuntu clang version 14.0.0-1ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
GodBolt behavior is the same (from clang 5.0 to 15.0, older versions produce a linker error).
Any ideas?
Thanks.
Code example I take from https://cplusplus.com/reference/fstream/ifstream/open/
So, this I've tried to run with/without
char c = ifs.get();
Tried different version of the clang.

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.

Why is valgrind reporting errors for libstdc++'s std::locale?

Related question: wifstream with imbue, locale produces valgrind errors
I am using cppreference's (potentially flawed) examples, in particular the one present on their imbue page. Using the command line on the coliru online compiler:
clang++ -std=c++14 -stdlib=libstdc++ -O3 -Wall -Wextra -pedantic-errors
-pthread main.cpp && valgrind ./a.out
the following test cases produce errors like these (unless I state "no errors"):
==5421== Invalid read of size 8
==5421== at 0x590CBC0: wcscmp (wcscmp.S:208)
==5421== by 0x4EAC174: std::moneypunct<wchar_t, false>::~moneypunct() (monetary_members.cc:927)
==5421== by 0x4EAC1D8: std::moneypunct<wchar_t, false>::~moneypunct() (monetary_members.cc:932)
==5421== by 0x4EA1695: std::locale::_Impl::~_Impl() (locale_classes.h:412)
==5421== by 0x4EA17D8: std::locale::~locale() (locale_classes.h:521)
==5421== by 0x400955: main (in /tmp/1412433400.2497/a.out)
==5421== Address 0x5c2e0b8 is 0 bytes after a block of size 8 alloc'd
==5421== at 0x4C2AC27: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5421== by 0x4EABE61: std::moneypunct<wchar_t, false>::_M_initialize_moneypunct(__locale_struct*, char const*) (monetary_members.cc:847)
==5421== by 0x4EA3CD7: std::locale::_Impl::_Impl(char const*, unsigned long) (locale_facets_nonio.h:993)
==5421== by 0x4EA406A: std::locale::locale(char const*) (localename.cc:42)
==5421== by 0x40094D: main (in /tmp/1412433400.2497/a.out)
cppreference's imbue example:
#include <iostream>
#include <sstream>
#include <locale>
int main()
{
std::istringstream iss;
iss.imbue(std::locale("en_US.UTF8"));
std::cout << "Current locale: " << iss.getloc().name() << '\n';
iss.imbue(std::locale());
std::cout << "Global locale : " << iss.getloc().name() << '\n';
}
libstdc++ - errors
libc++ - no errors
The linked questioned at the top's reduced example:
#include <iostream>
#include <locale>
int main (int argc, char **argv) {
try {
std::locale * l1 = new std::locale("de_DE.UTF-8");
delete l1;
std::locale l2("de_DE.UTF-8");
} catch(...) {
return 0;
}
return 0;
};
libstdc++ - errors
libc++ - no errors
Linked bug report in above question's reduced example:
#include <wchar.h>
void foo(int)
{
}
int main()
{
wchar_t *a=new wchar_t[2], *b=new wchar_t[2];
size_t j;
a[0]=b[0]='A';
a[1]=b[1]=0;
foo(wcscmp(a, b));
delete[] a;
delete[] b;
return 0;
}
libstdc++ - no errors
libc++ - no errors
I've added the bug report's test case for completeness even though they produce no errors. Coliru's valgrind version is 3.7.0, and OP in the linked thread mentions upgrading to 3.8.1 and still receiving errors. I'm not on a Linux machine at the moment, and so cannot test it myself. If it makes any difference, here's glibc output:
GNU C Library (Ubuntu EGLIBC 2.15-0ubuntu10.7) stable release version 2.15, by Roland McGrath et al.
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.6.3.
Compiled on a Linux 3.2.60 system on 2014-08-28.
Available extensions:
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.
Where does the bug lie? cppreference's examples, valgrind, or libstdc++?
It's a false positive in Valgrind, fixed here.

How does one get the actual function names from these output

I use boost test for unit testing and gcov and lcov for measuring the coverage.
Unfortuanlly genhtml generates reports like that for function coverage:
I now want to know what the function _ZN7UtilLib11ProgressBarC2EjdRSo actually is.
So far I can not correlate this function to any of the class interface of ProgressBar:
class ProgressBar {
public:
explicit ProgressBar(
unsigned int expected_count,
double updateInterval = 30,
std::ostream& os = std::cout);
unsigned int operator+=(unsigned int increment);
unsigned int operator++();
unsigned int operator++(int i);
}
Can any one help me how to either get better function names with gcov or how does one understand these function names.
The application is compiled with gcc4.7 with the following flags:-g -g -save-temps=obj -Wall -Wextra -Wno-unused-parameter -Wno-error=unused-parameter -O0 -pedantic
These are mangled C++ symbols, use c++filt in a shell to demangle it:
> c++filt _ZN7UtilLib11ProgressBarC2EjdRSo
UtilLib::ProgressBar::ProgressBar(unsigned int, double, std::basic_ostream<char, std::char_traits<char> >&)
Also, since you seem to use genhtml, check out the --demangle-cpp option to do the demangling automatically for you.
Note that the compiler emits two implementations for the ctor you wrote, using --demangle-cpp will hide the difference which is visible only in the mangled symbol name. To understand what the compiler is doing, have a look here.
Use c++filt, like this:
$c++filt -n _ZN7UtilLib11ProgressBarC2EjdRSo
which outputs:
UtilLib::ProgressBar::ProgressBar(unsigned int, double, std::basic_ostream<char, std::char_traits<char> >&)