Как исправить ошибку сегментации для клиента Ansi C Tcp? - PullRequest
0 голосов
/ 30 апреля 2020

Я пытаюсь расширить пример клиента Tcp, разработанного с использованием Ansi C, следуя книге "Сокеты TCP / IP в C". Клиент подключается к Tcp-серверу, предоставляя строки различной длины в зависимости от запроса, предоставленного клиентом (я разработал свой собственный простой протокол). Когда возвращаемые строки имеют короткую длину, все работает нормально. Когда они превышают определенную длину (это происходит, например, с 4 КБ), клиент аварийно завершает работу с ошибкой сегментации.

Сокет обрабатывается с помощью оболочки для потокового ввода / вывода:

FILE *str = fdopen(sock, "r+"); // Wrap for stream I/O

И передача и прием обрабатываются с использованием fwrite () и fread ().

Это вызов, который генерирует ошибку в моем проекте (вызывающая сторона):

uint8_t inbuf[MAX_WIRE_SIZE];
size_t respSize = GetNextMsg(str, inbuf, MAX_WIRE_SIZE); // Get the message

И это реализация функции GetNextMsg (), которая использует для получения и расфасовки данных:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <netinet/in.h>
#include "Practical.h"

/* Read 4-byte length and place in big-endian order.
 * Then read the indicated number of bytes.
 * If the input buffer is too small for the data, truncate to fit and
 * return the negation of the *indicated* length.  Thus a negative return
 * other than -1 indicates that the message was truncated.
 * (Ambiguity is possible only if the caller passes an empty buffer.)
 * Input stream is always left empty.
 */
uint32_t GetNextMsg(FILE *in, uint8_t *buf, size_t bufSize)
{
    uint32_t mSize = 0;
    uint32_t extra = 0;

    if (fread(&mSize, sizeof(uint32_t), 1, in) != 1)
        return -1;
    mSize = ntohl(mSize);
    if (mSize > bufSize)
    {
        extra = mSize - bufSize;
        mSize = bufSize; // Truncate
    }
    if (fread(buf, sizeof(uint8_t), mSize, in) != mSize)
    {
        fprintf(stderr, "Framing error: expected %d, read less\n", mSize);
        return -1;
    }
    if (extra > 0)
    { // Message was truncated
        uint32_t waste[BUFSIZE];
        fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel
        return -(mSize + extra);                  // Negation of indicated size
    }
    else
        return mSize;
}

Я подозреваю, что это может быть связано с тем, что с Tcp отправитель и получатель обрабатывают данные с потоковым поведением, поэтому не гарантируется, что получатель получает все данные одновременно, как, вероятно, предположил простой пример, из которого я начал. На самом деле с короткими струнами все работает. С более длинными строками это не так.

Я сделал упрощенную отладку, вставив printf в качестве первой вещи внутри функции, но когда у меня есть cra sh, это даже не печатается ,

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

Размер буферов намного больше размера длина сообщения, вызывающего проблему (1 МБ против 4 КБ). Я даже пытался увеличить размер буфера сокета с помощью setsockopt:

int rcvBufferSize;
    // Retrieve and print the default buffer size
    int sockOptSize = sizeof(rcvBufferSize);
    if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize, (socklen_t*)&sockOptSize) < 0)
        DieWithSystemMessage("getsockopt() failed");
    printf("Initial Receive Buffer Size: %d\n", rcvBufferSize);

    // Double the buffer size
    rcvBufferSize *= 10;
    if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvBufferSize, 
sizeof(rcvBufferSize)) < 0)
        DieWithSystemMessage("setsockopt() failed");

, но это не помогло.

Любые идеи о причине и как я могу это исправить

1 Ответ

1 голос
/ 30 апреля 2020

Этот код:

 { // Message was truncated
    uint32_t waste[BUFSIZE];
    fread(waste, sizeof(uint8_t), extra, in); // Try to flush the channel

считывает extra байтов в буфер размером 4*BUFSIZE (4, потому что вы намеревались создать буфер unit8_t, но вместо этого случайно сделали его uint32_t) .

Если extra больше 4*BUFSIZE, то у вас будет переполнение локального буфера и повреждение стека, что может привести к взлому sh.

Чтобы сделать это правильно, что-то вроде этого нужно:

  int remaining = extra;
  while (remaining > 0) {
    char waste[BUFSIZE];
    int to_read = min(BUFSIZE, remaining);
    int got = fread(waste, 1, to_read, in);
    if (got <= 0) break;
    remaining -= got;    
  }
...