`string s = std::to_string(1) + std::to_string(2)` 会使用未初始化的内存吗?

3

问题是,以下代码片段使用未初始化的内存吗?这是由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

这里也开放了一个镜像问题 (点击此处)


2
这是完全有效的代码。标准库实现可能采用优化技术,这可能会触发卫士程序。但这并不意味着实现有缺陷。它可能只是在库代码中忘记应用注释,以使卫士程序忽略“问题”。 - StoryTeller - Unslander Monica
对于这些短字符串,有短字符串优化。我不确定GCC中如何实现它。但是,由于它们的大小<= 2,因此在字符串中可能未初始化某些字节,然后复制操作可能会处理这些未初始化的值,这并不奇怪。 - ALX23z
@ALX23z 其实我尝试了更长的代码,像是 string s1 = to_string(111) + to_string(222);,但还是会触发警告。而且为了使用MemorySanitizer,我必须使用 clang++ - D.J. Elkind
@StoryTeller-UnslanderMonica,实际上我之前曾与另一个人争论过这个问题,但未能得出结论(不幸的是我找不到那篇文章了)。我正在查看C17/C18标准此处。我认为uint32_t a, b; uint32_t c = a + b;将使c成为“未指定值”,如第3.19.3节所定义。但标准在哪里说这会触发UB呢? - D.J. Elkind
1
由于这是C++,相关部分在https://timsong-cpp.github.io/cppwp/n4868/basic.indet#2 - 当对存在UB的所有类型(除了“unsigned char”或“std :: byte”)进行概括性陈述时,它适用。我不记得C标准在哪里说过,但我记得在C11中看到了类似的措辞。 - StoryTeller - Unslander Monica
显示剩余4条评论
1个回答

0

这个问题的镜像问题已经得到了回答。回答的要点是MemorySanitizer不是一种“插件”式检查器。

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

这篇官方维基文章描述了如何实现它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接