clang++ 内存清理程序报告未初始化值的使用

问题描述 投票:0回答:2

此代码取自IncludeOS github页面。我对其进行了一些修改,使其无需其他头文件即可编译。 IncludeOS 中的

find
函数有点过于冗长,所以我想简化它。但修改后,代码的行为与我预期的不同。

这是一个简短的解释。此代码用于解析 HTTP 标头。标头字段是名称-值对。它表示为

vector<pair<string, string>>
find
函数用于查找标题中字段名称的位置,
has_field
用于检查标题中是否存在特定字段名称。

main
函数中,四个元素被附加到字段中。
six
不应在字段中找到。但是
has_field
返回 true。

我尝试使用

gdb
来跟踪错误。但我迷失在产出的海洋中。我确实发现了一条有点有趣的消息。

std::__uninitialized_copy::__uninit_copy<__gnu_cxx::__normal_iterator、std::allocator >、std::__cxx11::basic_string、std::allocator > > const*、std::vector >、std::__cxx11:: basic_string、std::allocator > >、std::allocator >、std::__cxx11::basic_string、std::allocator > >>>>、std::pair、std::allocator > , std::__cxx11::basic_string,std::allocator > >*> (__first={first = "one", Second = "1"}, __last=

{第一个=,第二个=“”},__结果=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;
}
c++ initialization clang++ memory-sanitizer
2个回答
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

1
投票

Clang 的内存清理程序似乎也需要对外部库 (libstrdc++) 进行检测,否则可能会出现误报。

https://clang.llvm.org/docs/MemorySanitizer.html#handling-external-code

“MemorySanitizer 要求对所有程序代码进行检测。这还包括程序依赖的任何库,甚至是 libc。”

因此 valgrind 似乎对于未初始化值检测来说仍然是最实用的(尽管很慢)。

© www.soinside.com 2019 - 2024. All rights reserved.