Search code examples
c++pointersvalgrindstdstring

valgrind memory access error when accessing result of string::c_str()


So I was writing some networking code when I ran into the strangest memory issue and I cant properly sort out what might be going on here. I am wondering if there might be some sort of implication in c_str() that I am not properly observing.

So here is the code with the error in it. (There is also a freeing error but I made this function as just a pet project).

#include <netdb.h>       // for AF_UNSPEC, AF_INET, AF_INET6
#include <stdint.h>      // for uint16_t, etc.
#include <sys/types.h>   // for AF_UNSPEC, AF_INET, AF_INET6
#include <sys/socket.h>  // for AF_UNSPEC, AF_INET, AF_INET6
#include <string>        // for std::strin
#include <stdio.h> 
#include <string.h>
int main() {
    uint16_t port = 2098;


    struct addrinfo hints;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_INET6;       // IPv6 (also handles IPv4 clients)
    hints.ai_socktype = SOCK_STREAM;  // stream
    hints.ai_flags = AI_PASSIVE;      // use wildcard "INADDR_ANY"
    hints.ai_protocol = IPPROTO_TCP;  // tcp protocol
    hints.ai_canonname = nullptr;
    hints.ai_addr = nullptr;
    hints.ai_next = nullptr;

    const char* port_num = (std::to_string(port)).c_str();

    struct addrinfo *result;
    int res = getaddrinfo(nullptr, port_num, &hints, &result);

    printf("HI\n");
}

If you valgrind the resulting binary you get:

==45919== Invalid read of size 1
==45919==    at 0x573BA5C: getaddrinfo (in /usr/lib64/libc-2.17.so)
==45919==    by 0x400C95: main (in /homes/iws/kieruc/Coding/a.out)
==45919==  Address 0x5a22058 is 24 bytes inside a block of size 29 free'd
==45919==    at 0x4C2B16D: operator delete(void*) (vg_replace_malloc.c:576)
==45919==    by 0x4EF3B62: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.19)
==45919==    by 0x400C7C: main (in /homes/iws/kieruc/Coding/a.out)
==45919==  Block was alloc'd at
==45919==    at 0x4C2A1E3: operator new(unsigned long) (vg_replace_malloc.c:334)
==45919==    by 0x4EF3A18: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.19)
==45919==    by 0x400FFE: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&, std::forward_iterator_tag) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400F14: char* std::string::_S_construct_aux<char*>(char*, char*, std::allocator<char> const&, std::__false_type) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400EDD: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400E8F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400E20: std::string __gnu_cxx::__to_xstring<std::string, char>(int (*)(char*, unsigned long, char const*, __va_list_tag*), unsigned long, char const*, ...) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400CDE: std::to_string(int) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400C60: main (in /homes/iws/kieruc/Coding/a.out)
==45919== 
==45919== Invalid read of size 1
==45919==    at 0x573BACA: getaddrinfo (in /usr/lib64/libc-2.17.so)
==45919==    by 0x400C95: main (in /homes/iws/kieruc/Coding/a.out)
==45919==  Address 0x5a22058 is 24 bytes inside a block of size 29 free'd
==45919==    at 0x4C2B16D: operator delete(void*) (vg_replace_malloc.c:576)
==45919==    by 0x4EF3B62: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib64/libstdc++.so.6.0.19)
==45919==    by 0x400C7C: main (in /homes/iws/kieruc/Coding/a.out)
==45919==  Block was alloc'd at
==45919==    at 0x4C2A1E3: operator new(unsigned long) (vg_replace_malloc.c:334)
==45919==    by 0x4EF3A18: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.19)
==45919==    by 0x400FFE: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&, std::forward_iterator_tag) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400F14: char* std::string::_S_construct_aux<char*>(char*, char*, std::allocator<char> const&, std::__false_type) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400EDD: char* std::string::_S_construct<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400E8F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<char*>(char*, char*, std::allocator<char> const&) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400E20: std::string __gnu_cxx::__to_xstring<std::string, char>(int (*)(char*, unsigned long, char const*, __va_list_tag*), unsigned long, char const*, ...) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400CDE: std::to_string(int) (in /homes/iws/kieruc/Coding/a.out)
==45919==    by 0x400C60: main (in /homes/iws/kieruc/Coding/a.out)

However if I change the code to this

std::string portstr = std::to_string(port);

struct addrinfo *result;
int res = getaddrinfo(nullptr, portstr.c_str(), &hints, &result);

Then there are no memory errors. And I dont quite understand why.

Another thing to note is that if I try to compile the version with the const char* variable with char* instead you get a warning that the variable should be constant.

What is going on here?


Solution

  • The buffer returned by c_str is valid only as long as the associated std::string object lives. And here

    const char* port_num = (std::to_string(port)).c_str();
    

    A temporary string object is created, its buffer address is taken, and then it dies at the end of the full expression. You get a memory error for using a dangling pointer.

    If you wish to use a temporary std::string, it must be created during the full expression where you use the buffer:

    int res = getaddrinfo(nullptr, std::to_string(port).c_str(), &hints, &result)