2017-11-25 91 views
0

此代码取自IncludeOS github page。我修改了一下,这样它就可以在没有其他头文件的情况下编译。来自IncludeOS的find函数有点太冗长,所以我想简化它。但修改后,代码的行为与我所期望的不同。铛++内存消毒剂报告使用未初始化值

这里是一个简短的解释。此代码用于解析HTTP标头。标题字段是名称 - 值对。它代表vector<pair<string, string>>find函数用于查找标题中的字段名称的位置,并且has_field用于检查标题中是否存在特定的字段名称。

main函数中,四个元素被添加到字段。 six不应在fields.But has_field返回true。

我试图跟踪gdb的错误。但是我在输出的海洋中迷失了。我确实发现了一些有趣的信息。

的std :: __ uninitialized_copy <假> :: __ uninit_copy < __gnu_cxx :: __ normal_iterator <的std ::对<的std :: __ cxx11 :: basic_string的<炭的std :: char_traits <炭>,性病::分配器<炭> >,性病:: __ cxx11 :: basic_string的<炭的std :: char_traits <炭>,性病::分配器<炭> > >常量*,性病::矢量<的std ::对<的std :: __ cxx11 :: basic_string的<焦,标准:: char_traits <焦炭<,性病::分配器<焦炭> >,性病:: __ cxx11 :: basic_string的<焦,标准:: char_traits <焦炭>,性病::分配器<焦炭> > > ,性病::分配器<的std ::对<的std :: __ cxx11 :: basic_string的<炭的std :: char_traits <炭<,性病::分配器<炭> >,性病:: __ cxx11 :: basic_string的<炭,标准: :char_traits <char>,std :: allocator <char> > > > > >,性病::对<的std :: __ cxx11 :: basic_string的<炭的std :: char_traits <炭>,性病::分配器<炭> >,性病:: __ cxx11 :: basic_string的<炭,标准:: char_traits <炭>,性病::分配器<炭> > > > *(__first = {第一= “一”,第二= “1”},__last =

{第一= <错误读取变量:不能创建一个懒惰的字符串地址0x0和非零长度。 >,第二= “”},__result = 0x61bf00)

我以前clang消毒找出什么是错的。只有记忆消毒剂显示有趣的报告。运行,

clang++ -std=c++17 -O1 -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer main.cc

/a.out报告,

未初始化的值通过在函数“_ZNSt4pairINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES5_EC2IRA6_KcRA2_S8_Lb1EEEOT_OT0_'`堆栈帧 'ref.tmp' 的分配创建。

但是,当优化级别设置为-O3时,什么也没有显示出来。

#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; 
} 
+1

不知道有未初始化值,但是按值传递的'fields'向量,从而'has_field'迭代器比较到载体的不同副本。 –

+0

@BoPersson你是对的。将一个迭代器返回给一个通过值传递的容器是非常错误的。这里是一个相关的stackoverflow问题。 https://stackoverflow.com/questions/10113572/does-passing-containers-by-value-invalidate-iterators – JohnKoch

回答

1

这很可能是误报。 llvm带有符号化器二进制文件,它允许清洁剂输出行号。我已经成功地再现您的错误与此小例子:

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 } 

在这两种情况下,堆栈跟踪声称std::endl是罪魁祸首。对于发生错误以下神奇的事情已经发生:

  • 输出std::endl
  • 有两个函数调用

如果我宣布a采取参照fields,错误消失; b也不能这么说。所有这些让我相信这是无意义的,而且是误报。作为参考,这里的消毒液输出和行号:

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 
+0

这可能是误报。我再次用'valgrind --tool = memcheck --track-origins = yes。/ a.out'来检查它。什么都没有出现。 Bo-Persson发现我的程序存在问题。我按值传递一个向量,并且我返回一个迭代器,这非常糟糕。 – JohnKoch