Ошибка доступа к памяти в valgrind при доступе к результату string :: c_str () - PullRequest
3 голосов
/ 07 июня 2019

Итак, я писал какой-то сетевой код, когда столкнулся с самой странной проблемой с памятью, и я не могу правильно разобраться, что здесь может происходить. Мне интересно, может быть в c_str() может быть какое-то значение, которое я не правильно наблюдаю.

Так вот код с ошибкой в ​​нем. (Существует также ошибка освобождения, но я сделал эту функцию как проект для домашних животных).

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

Если вы вальгриндируете полученный бинарный файл, вы получите:

==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)

Однако, если я изменю код на этот

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

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

Тогда нет ошибок памяти. И я не совсем понимаю, почему.

Еще одна вещь, на которую следует обратить внимание: если я попытаюсь скомпилировать версию с переменной const char* с помощью char*, вместо этого вы получите предупреждение о том, что переменная должна быть постоянной.

Что здесь происходит?

Ответы [ 2 ]

4 голосов
/ 07 июня 2019

С помощью const char* port_num = (std::to_string(port)).c_str(); вы создаете временный объект типа std::string, который будет жить столько же, сколько и выражение, в котором он используется. Таким образом, .c_str() будет указывать на память, которая будет освобождена сразу после вашего утверждения.

Сделайте из него две строки:

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

Таким образом, portStr -объект будет жить до конца функции, и вы можете использовать результат .c_str -вызова до конца функции (если вы не изменили protStr между) .

4 голосов
/ 07 июня 2019

Буфер, возвращаемый c_str, действителен только в течение всего времени жизни связанного объекта std::string. И вот

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

A временный строковый объект создается, его адрес буфера берется, а затем он умирает в конце полного выражения. Вы получаете ошибку памяти за использование висячего указателя.

Если вы хотите использовать временный std::string, он должен быть создан во время полного выражения, где вы используете буфер:

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