Search code examples
c++arraysstringstrlenaddress-sanitizer

std::string constructed from subrange of char array calls strlen


It is similar to LeetCode C++ Convert char[] to string, throws AddressSanitizer: stack-buffer-overflow error

The code is

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

Execution ends up with address sanitizer complaining about strlen's stack-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)

I'd expect that std::string s{buf, 2, 3}; calls a constructor overload with known bounds (start at 2, length is 3). Why is it calling strlen()? Which overload is used?


Solution

  • Check cpp insights. It is great tool to see what was used during overload resolution.

    It generates this:

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

    After cleaning up to make this more readable:

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

    So note that buf is first converted to std::string and this conversion requires strlen. Since your array do not contain terminating zero buffer overflow happens.