【问题标题】:std::string constructed from subrange of char array calls strlen从 char 数组的子范围构造的 std::string 调用 strlen
【发布时间】:2022-01-04 16:41:49
【问题描述】:

类似于LeetCode C++ Convert char[] to string, throws AddressSanitizer: stack-buffer-overflow error

代码是

#include <string>

int main() {
    char buf[10] = {6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
    std::string s{buf, 2, 3};
    return 0;
}

执行结果是地址清理程序抱怨strlenstack-buffer-overflow

$ clang++ -g -fsanitize=address foo.cpp ; ./a.out
=================================================================
==1001715==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd76b2510a at pc 0x00000042f029 bp 0x7ffd76b250b0 sp 0x7ffd76b24870
READ of size 23 at 0x7ffd76b2510a thread T0
    #0 0x42f028 in strlen (/tmp/a.out+0x42f028)
    #1 0x7fd6de786e9b in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x145e9b)
    #2 0x4c6cfe in main /tmp/foo.cpp:6:19
    #3 0x7fd6de2d60b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
    #4 0x41c3fd in _start (/tmp/a.out+0x41c3fd)

我希望std::string s{buf, 2, 3}; 调用具有已知边界的构造函数重载(从 2 开始,长度为 3)。为什么叫strlen()?使用了哪个重载?

【问题讨论】:

    标签: c++ arrays string strlen address-sanitizer


    【解决方案1】:

    Check cpp insights。这是查看在重载解决期间使用了什么的好工具。

    它会生成这个:

    #include <string>
    
    int main()
    {
      char buf[10] = {6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
      std::string s = std::basic_string<char, std::char_traits<char>, std::allocator<char> >{std::basic_string<char, std::char_traits<char>, std::allocator<char> >(buf, std::allocator<char>()), 2, 3};
      return 0;
    }
    

    清理后使其更具可读性:

    #include <string>
    
    int main()
    {
      char buf[10] = {6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
      std::string s = std::string{std::string(buf), 2, 3};
      return 0;
    }
    

    所以请注意,buf 首先转换为std::string,而此转换需要strlen。由于您的数组不包含终止零缓冲区溢出发生。

    【讨论】:

    • [string.cons]/6 表示使用了临时的string_view,而不是string。我想不可能观察到差异,但string 具有误导性,因为没有发生实际的堆分配。
    • 是的,因为应该使用 C++17 constructor nr 11(应该赢得重载决议)。但是工具说即使选择了 C++2a 也不会使用它。还从地址清理程序调用堆栈指向std::string{buf, allocator()}godbolt.org/z/j63EaW33G 所以也许应该报告编译器的错误。
    • 会不会是堆栈出错了?我正在研究 libstdc++ 实现(herehere),看起来它做对了(使用 string_view)。
    • 天哪!首先认为这是一个 libstdc++ 错误,但 libc++ 做了同样的事情!我们是否应该就此提出一个新问题?
    【解决方案2】:

    HolyBlackCat 的 answer 和 Marek R 的 answer 解释了错误的原因。下面是一个使用 (pointer, count) 构造函数的解决方案:

    std::string s{buf + 2, 3};
    

    【讨论】:

      猜你喜欢
      • 2015-04-26
      • 1970-01-01
      • 1970-01-01
      • 2016-04-04
      • 1970-01-01
      • 1970-01-01
      • 2020-08-20
      • 1970-01-01
      相关资源
      最近更新 更多