Reputation: 1248
This code is taken from IncludeOS github page. I modified it a bit so that it compiles without other header files. find
function from IncludeOS is a bit too verbose, so I want to simplify it. But after modification, the code behaves differently from what I expected.
Here is a short explanation. This code is used to parse HTTP headers. Header fields are name-value pairs. It's represented as vector<pair<string, string>>
. find
function is used to find the location of a field name in the header, and has_field
is used to check whether a specific field name exists in the header.
In main
function, four elements are appended to fields. six
shouldn't be found in fields.But has_field
returns true.
I tried to track the error with gdb
. But I was lost in the sea of outputs. I did find a somewhat interesting message.
std::__uninitialized_copy<false>::__uninit_copy<__gnu_cxx::__normal_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const*, std::vector<std::pair<std::__cxx11::basic_string<char, std::char_traits<char<, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char<, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >>>>, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >*> (__first={first = "one", second = "1"}, __last=
{first = <error reading variable: Cannot create a lazy string with address 0x0, and a non-zero length.>, second = ""}, __result=0x61bf00)
I used clang
sanitizer to find out what's wrong. Only memory sanitizer shows interesting reports. Running,
clang++ -std=c++17 -O1 -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer main.cc
/a.out
reports,
Uninitialized value was created by an allocation of 'ref.tmp' in the stack frame of function '_ZNSt4pairINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES5_EC2IRA6_KcRA2_S8_Lb1EEEOT_OT0_'`.
When optimization level is set to -O3
, however, nothing shows up.
#include <algorithm>
#include <iostream>
#include <vector>
#include <experimental/string_view>
using Headers = std::vector<std::pair<std::string, std::string>>;
using string_view = std::experimental::string_view;
Headers::const_iterator find(Headers fields, const string_view field) {
if (field.empty()) return fields.cend();
//-----------------------------------
return
std::find_if(fields.cbegin(), fields.cend(), [field](const auto _) {
return std::equal(_.first.cbegin(), _.first.cend(), field.cbegin(), field.cend(),
[](const auto a, const auto b) { return std::tolower(a) == std::tolower(b); });
});
}
bool has_field(Headers fields, const string_view field)
{
return find(fields, field) != fields.cend();
}
int main()
{
Headers fields;
fields.emplace_back("one", "1");
fields.emplace_back("two", "2");
fields.emplace_back("three", "3");
fields.emplace_back("four", "4");
std::string s = "six";
if (has_field(fields, s))
std::cout << s << " is in " << "fields" << std::endl;
return 0;
}
Upvotes: 1
Views: 5830
Reputation: 11
It seems that Clang's memory sanitizer expects external libraries (libstrdc++) to also be instrumented otherwise false positives are likely.
https://clang.llvm.org/docs/MemorySanitizer.html#handling-external-code
"MemorySanitizer requires that all program code is instrumented. This also includes any libraries that the program depends on, even libc."
So valgrind still seems the most practical for uninitialized value detection (albeit slow).
Upvotes: 1
Reputation: 26
It's likely a false positive. llvm comes with the symbolizer binary, which allows the sanitizer to output line numbers. I've managed to reproduce your error with this minimal example:
1 #include <iostream>
2 #include <vector>
3
4 using Headers = std::vector<int>;
5
6 bool a(Headers fields) {
7 return true;
8 }
9
10 bool b(Headers fields)
11 {
12 return a(fields);
13 }
14
15 int main()
16 {
17 Headers fields;
18
19 if (b(fields)) {
20 std::cout << std::endl;
21 }
22
23 return 0;
24 }
In both cases, the stack trace claims std::endl
is the culprit. For the error to occur the following magical things have to occur:
std::endl
If I declare a
to take fields
by reference, the error disappears; the same cannot be said for b
. All of this leads me to believe it's nonsensical and a false positive. For reference, here's the sanitizer output with line numbers:
Uninitialized bytes in __interceptor_memcmp at offset 192 inside [0x7fff18347610, 256)
==5724==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x7f8f663d94ab in std::ctype<char>::_M_widen_init() const (/lib64/libstdc++.so.6+0xb74ab)
#1 0x7f8f66435d17 in std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) (/lib64/libstdc++.so.6+0x113d17)
#2 0x4912ff in main test.cpp:20:15
#3 0x7f8f65415889 in __libc_start_main (/lib64/libc.so.6+0x20889)
#4 0x41a9b9 in _start (a.out+0x41a9b9)
Uninitialized value was created by an allocation of 'ref.tmp' in the stack frame of function '_ZNSt6vectorIiSaIiEEC2ERKS1_'
#0 0x491360 in std::vector<int, std::allocator<int> >::vector(std::vector<int, std::allocator<int> > const&) /usr/bin/../lib/gcc/x86_64-redhat-linux/7/../../../../include/c++/7/bits/stl_vector.h:329
SUMMARY: MemorySanitizer: use-of-uninitialized-value (/lib64/libstdc++.so.6+0xb74ab) in std::ctype<char>::_M_widen_init() const
Exiting
Upvotes: 1