Неожиданное C поведение с указателями - PullRequest
0 голосов
/ 28 января 2020

Я пытаюсь позволить переменной char* указывать на строку mallo c, но происходит что-то действительно неправильное, и я имею дело с ошибкой сегментации.

Это вовлеченная часть моего кода, я попытался привести исполняемый пример, но с использованием сокета AF_ UNIX, боюсь, это не легко проверить.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdarg.h>

#define MSG_LEN 64

int vasprintf(char** str, const const char* format, va_list args) {
    size_t size;
    va_list tmp;
    va_copy(tmp, args);
    size = vsnprintf(NULL, 0, format, tmp);
    va_end(tmp);
    if (size == -1) {
        return -1;
    }
    if ((*str = (char*)malloc(sizeof(char) * (size + 1))) == NULL) {
        return -1;
    }
    size = vsprintf(*str, format, args);
    return size;
}

int asprintf(char** str, const char* format, ...) {
    size_t size;
    va_list args;
    if (str == NULL) {
        return 1;
    }
    va_start(args, format);
    size = vasprintf(str, format, args);
    va_end(args);
    return size;
}

typedef struct {
    int socket;
    struct sockaddr addr;
    socklen_t addrlen;
} connection_t;

int connection_init(connection_t* connection, const char* address, const uint16_t port) {
    if (!port) {
        if ((connection->socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            return -1;
        }
        memset(&connection->addr, 'x', sizeof(connection->addr));
        ((struct sockaddr_un*) & connection->addr)->sun_family = AF_UNIX;
        ((struct sockaddr_un*) & connection->addr)->sun_path[0] = '\0';
        strncpy(((struct sockaddr_un*) & connection->addr)->sun_path + 1, address, strlen(address));
        connection->addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(address);
        return 0;
    }
}

int connetcion_connect(const connection_t* connection) {
    if (connect(connection->socket, &connection->addr, connection->addrlen) == -1) {
        return -1;
    }
    return 0;
}

int connection_send(const connection_t* connection, const char* buff) {
    ssize_t len;
    if ((len = send(connection->socket, buff, strlen(buff), 0)) == -1) {
        return -1;
    }
    return len;
}

int connection_recv(const connection_t* connection, char** buff) {
    ssize_t len;
    if (((*buff) = (char*)malloc(sizeof(char) * (MSG_LEN + 1))) == NULL) {
        return -1;
    }
    memset((*buff), 0, sizeof(char) * (MSG_LEN + 1));
    len = recv(connection->socket, (*buff), MSG_LEN, 0);
    if (len == -1 || len > MSG_LEN) {
        free((*buff));
        (*buff) = NULL;
        return -1;
    }
    (*buff)[len] = 0;
    /*  Resize if string is shorter than len    */
    if (realloc((*buff), sizeof(char) * (strlen((*buff)) + 1)) == NULL) {
        free((*buff));
        (*buff) = NULL;
        return -1;
    }
    return len;
}

int foo(char* query, char** result) {
    connection_t con;
    char* filename;
    if (asprintf(&filename, "%s%s", getenv("HOME"), "/.directory/socket") == -1) {
        return 1;
    }
    if (connection_init(&con, filename, 0) == -1) {
        return 1;
    }
    free(filename);
    if (connetcion_connect(&con) == -1) {
        return 1;
    }
    if (connection_send(&con, query) == -1) {
        return 1;
    }
    printf("Address %p in foo point to:\n", result);
    printf("%p\n", *result);
    if (connection_recv(&con, result) == -1) {
        return 1;
    }
    printf("Address %p in foo point to:\n", result);
    printf("%p\n", *result);
    return 0;
}

int main(int argc, char *argv[]){
    if (argc == 3 && !strncasecmp(argv[1], "hello", 5)) {
        char* buff = NULL;
        printf("Address %p in main point to:\n", &buff);
        printf("%p\n", buff);
        if(foo(argv[2], &buff)){
            return 1;
        }
        printf("Address %p in main point to:\n", &buff);
        printf("%p\n", buff);
        printf("%s\n", buff);
    }
    return 0;
}

Ожидаемый результат:

Address "0x7ffd4c0c0d28" in main point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
0x1bfa6a0
Address "0x7ffd4c0c0d28" in main point to:
0x1bfa6a0

Фактический результат:

Address "0x7ffd4c0c0d28" in main point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
(nil)
Address "0x7ffd4c0c0d28" in foo point to:
0x1bfa6a0
Address "0x7f74656b6367" in main point to:
Segmentation fault

Единственное возможное объяснение, которое я могу думать, это то, что я как-то повредил стек, если нужны другие ресурсы, пожалуйста, дайте мне знать, и я постараюсь предоставить их, как только возможно

Дополнительная информация:

Попробуйте printf("%s\n", *result) непосредственно перед return 0 Я получаю ожидаемую строку.

Сервер использует ту же connection_* функция для связи с клиентом.

Я включен на RHEL8.1 и собираю его с помощью g cc 8.3.1

1 Ответ

1 голос
/ 28 января 2020

Ваш connection_t просто имеет простой элемент struct sockaddr addr, который является просто базовой структурой. Необходимый фактический размер данных зависит от протокола. Когда я запускаю ваш код, con выделяется в… 29a0 и имеет 24 байта, но strncpy пытается записать 41 байт в… 29a7, таким образом переопределяя структуру. Вам необходимо выделить память для конкретной c структуры, необходимой для протокола. Вероятно, ваш addr член должен быть указателем, и вы должны выделить для него память, установить указатель так, чтобы он указывал на эту память, и настроить другой код.

...