Обнаружено разрушение стека при возврате функции

У меня есть функция для чтения TCP-пакетов неизвестного размера. Функция использует MSG_PEEK, чтобы найти необходимый размер полученного сообщения JSON (символ \"} в условии цикла), а затем запускает цикл еще раз без MSG_PEEK для очистки буфера. По какой-то причине два вывода в конце функции работают отлично, но когда я вызываю fn, моя программа сразу после возврата умирает и печатает «*** обнаружено разрушение стека ***: прекращено». content_buffer не должно переполняться, потому что я выделяю content_size + 1 и передаю content_size в recv, верно?

char *read_socket_raw(int socket)
{
    if (socket == -1)
    {
        fprintf(stderr, "Reading socket failed.\n");
        return NULL;
    }

    char *content_buffer = NULL;
    unsigned short content_size;

    why2_bool empty_buffer = 0; //WHETHER MSG_PEEK SHOULD BE USER OR NOT

    do
    {
        //FIND THE SENT SIZE
        content_size = 0;
        if (ioctl(socket, FIONREAD, &content_size) < 0 || content_size <= 0) continue;

        //ALLOCATE
        content_buffer = realloc(content_buffer, content_size + 1);

        read_section:

        //READ JSON MESSAGE
        if (recv(socket, content_buffer, content_size, !empty_buffer ? MSG_PEEK : 0) != content_size) //READ THE MESSAGE BY CHARACTERS
        {
            fprintf(stderr, "Socket probably read wrongly!\n");
        }

        if (empty_buffer) goto return_section; //STOP LOOPING
    } while (content_buffer == NULL || strncmp(content_buffer + (content_size - 2), "\"}", 2) != 0);

    //REMOVE JUNK FROM BUFFER (CUZ THE MSG_PEEK FLAG)
    empty_buffer = 1;
    goto read_section;

    return_section:

    content_buffer[content_size] = '\0';

    printf("'%s'\n", content_buffer);
    printf("'%p'\n\n", content_buffer);

    return content_buffer;
}

Что такое why2_realloc? Покажите, пожалуйста, минимально воспроизводимый пример . Примечание: использование вами goto затрудняет выполнение программы. Старайтесь избегать прыжков в циклы снаружи и т. д.

Ted Lyngmo 27.08.2024 19:44

Если вызов ioctl завершится неудачно или content_size <= 0, вы выйдете из цикла и повторно войдете в него (с помощью goto read_section;), не выделяя буфер. бум

Ted Lyngmo 27.08.2024 19:46

Извините, вы также можете передать стандартное перераспределение. Моя версия realloc использует только мой сборщик мусора. :)

ENGO_150 27.08.2024 19:46

«вы также можете передать стандартный realloc» — лучше, если вы предоставите минимальный воспроизводимый пример. Если каждый, кто читает вопрос, должен будет это сделать, будет много разных версий программы.

Ted Lyngmo 27.08.2024 19:48

Также см. мой второй комментарий, в котором указан один из возможных способов сбоя.

Ted Lyngmo 27.08.2024 19:49

Вам не обязательно знать, сколько байт доступно, прежде чем читать. Особенно если вы собираетесь продолжать чтение до тех пор, пока не получите все нужные данные — с таким же успехом вы можете просто выполнить блокирующее чтение. Обратите внимание, что при таком чтении может быть передано меньше байтов, чем запрошено, и, как правило, это будет сделано, если можно передать хотя бы один байт без блокировки, но не полное запрошенное число.

John Bollinger 27.08.2024 19:52

Однако учтите, что ваш подход хрупок. TCP не имеет встроенных границ сообщений, поэтому у вас, вероятно, возникнут проблемы, если сообщения JSON будут получены достаточно близко друг к другу, и вы не очистите одно из них до того, как начало следующего попадет в очередь приема.

John Bollinger 27.08.2024 19:54

Я не против периодического использования goto для пояснения, но использование goto для перехода в цикл извне — это извращение. Скорее запутывает, чем уточняет.

John Bollinger 27.08.2024 19:56

TCP-пакетов не существует, TCP — это поток. Даже если отправитель отправляет TCP по частям, они могут быть доставлены как одна часть или разделены в любом месте. Поэтому используйте буфер и читайте до sizeof(buffer) байт. Результат сообщает, сколько байт вы получили или произошла ли ошибка. Функции select() и poll() можно использовать для проверки наличия ожидающих данных.

Wiimm 27.08.2024 21:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
9
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашей функции есть что не нравится, но единственное, что кажется совершенно неправильным, — это тип переменной content_size. FIONREAD ioctl ожидает указатель на int, но вместо этого вы предоставили указатель на unsigned short. Неопределенные результаты поведения.

На практике ваш unsigned short, скорее всего, меньше int, и в этом случае ioctl() предположительно попытается записать за пределы этого объекта. Это как раз тот случай, который может вызвать сообщение о разрушении стека и сбой, поскольку, если хранилище для этой переменной находится в начале (верху) кадра стека функции, то выход за его границы действительно приведет к разрушению стека.

Другие вещи, которые не нравятся:

  • необоснованное использование goto. Я не фанатик goto, но на практике структурированные конструкции управления потоком почти всегда предпочтительнее. И я никогда не сталкивался с использованием goto, с которым я бы согласился, что (i) прыгнул назад или (ii) прыгнул в цикл или условное выражение извне.

  • вы создаете себе ненужные проблемы и сложности, пытаясь определить длину сообщения перед его прочтением. Вам не нужно этого делать. Просто выделите буфер, достаточно большой для большинства сообщений, и читайте в него все, что сможете. Перераспределите и прочтите еще немного, если вы заполните буфер чтения, не получив полного сообщения. Это не будет проблемой, если входящее сообщение будет меньше буфера — вы просто получите короткое чтение.

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

Огромное спасибо, чувак, изменение типа данных на int исправило это. Я постараюсь исправить переход, мне он тоже не нравится, если честно. Кроме того, у меня уже есть код, проверяющий конец сообщения вне функции read_socket_raw. Что касается длины, я просто думаю, что это самое элегантное решение, потому что у меня нет установленного ограничения на длину сообщения, и я не хочу этого делать. Отмечаем как решенную. Спасибо.

ENGO_150 27.08.2024 22:37

Другие вопросы по теме