【问题标题】:Does `string s = std::to_string(1) + std::to_string(2)` use uninitialized memory`string s = std::to_string(1) + std::to_string(2)` 使用未初始化的内存吗
【发布时间】:2023-02-23 12:36:50
【问题描述】:

问题是,下面的 sn-p 是否使用了未初始化的内存,如 Google 的MemorySanitizer 所报告的那样?还是误报?:

  • main.cpp:
#include <string>
#include <iostream>

using namespace std;

int main() {
    string s0 = to_string(1);
    cout << "s0: " << s0 << endl;
    string s1 = to_string(1) + to_string(2);
    cout << "s1: " << s1 << endl;
    return 0;
}
  • Makefile:
main:
    clang++ -fsanitize=memory -fsanitize-memory-track-origins -fPIE -pie -fno-omit-frame-pointer -g -O2 main.cpp -o main-msan.out
    clang++ -O2 main.cpp -o main.out

结果:

./main-msan.out 
s0: 1
==122092==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x55a7354e5cf7 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > std::operator+<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/basic_string.h:6123:34
    #1 0x55a7354e5cf7 in main <my_directory>/msan/main.cpp:9:30
    #2 0x7f201f6edd09 in __libc_start_main csu/../csu/libc-start.c:308:16
    #3 0x55a735468349 in _start (<my_directory>/msan/main-msan.out+0x21349)

  Uninitialized value was created by an allocation of 'ref.tmp' in the stack frame of function 'main'
    #0 0x55a7354e4d90 in main <my_directory>/msan/main.cpp:6

SUMMARY: MemorySanitizer: use-of-uninitialized-value /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/basic_string.h:6123:34 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > std::operator+<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&)
Exiting

镜像问题也开here

【问题讨论】:

  • 这是完全有效的代码。标准库实现可能会采用优化技术来阻止消毒剂。但这并不自动意味着实施有问题。它可能就像忘记在库代码中应用注释一样简单,以便消毒程序忽略“问题”。
  • 有针对这种短字符串的短字符串优化。我不确定它是如何在 GCC 中实现的。但是有些字节没有在字符串中初始化是不合理的,因为它们的大小 <= 2,然后复制可能会解决这些未初始化的值。
  • @ALX23z 实际上我尝试了更长的时间,比如string s1 = to_string(111) + to_string(222);,它仍然会触发投诉。同样为了让 MemorySanitizer 工作,我必须使用clang++
  • @StoryTeller-UnslanderMonica 这是我在想的另一点。假设我有以下内容:uint32_t a, b; uint32_t c = a + b; cout &lt;&lt; c &lt;&lt; endl; 我的理解是这段代码是有效的,它不会调用任何 UB,因为 unsigned int 永远不会溢出。诚然,c 的值可以是实现定义的或不确定的——但如果不知何故,如果我只需要一个值,但不关心值是什么,它应该可以正常工作。
  • 由于这是 C++,因此相关部分是 timsong-cpp.github.io/cppwp/n4868/basic.indet#2 - 它适用于所有类型(unsigned charstd::byte 除外),当对存在 UB 作出笼统声明时。我不记得 C 标准究竟在哪里说过,但我记得在 C11 中看到过这种效果的措辞。

标签: c++ memory-sanitizer


【解决方案1】:

mirror issue 这个问题得到了答案。答案的要点是 MemorySanitizer 不是“插件”式检查器。

具体来说,假设我们有一个源代码文件main.cpp,我们想将它编译成二进制main.out,仅仅在我的编译命令中添加-fsanitize=memory -fsanitize-memory-track-origins是不够的。为避免误报,main.out 使用的所有库也必须使用-fsanitize=memory -fsanitize-memory-track-origins 进行编译。在答案中,这被称为“仪器化”。

这个official wiki post 描述了我们如何实现它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 2017-12-23
    • 1970-01-01
    • 1970-01-01
    • 2014-05-11
    • 2020-12-03
    • 2013-10-07
    相关资源
    最近更新 更多