涉及指针的意外 C 行为

Unexpected C behavior involving pointers

我试图让一个 char* 变量指向一个 malloc 的字符串,但确实发生了一些错误,我正在处理一个分段错误。

那是我的代码涉及的部分,我试图给出一个可执行示例,但使用 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] = '[=10=]';
        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

我能想到的唯一可能的解释是我以某种方式损坏了堆栈,如果需要其他资源请告诉我,我会尽快提供它们

附加信息:

尝试在 return 0 之前 printf("%s\n", *result) 我得到预期的字符串。

服务器使用相同的connection_*功能与客户端通信。

我在 RHEL8.1 上,我正在用 gcc 8.3.1 编译它

您的 connection_t 只有一个普通的 struct sockaddr addr 成员,它只是基本结构。所需的实际数据大小因协议而异。当我 运行 你的代码时,con 分配在 ...29a0 并且有 24 个字节,但是 strncpy 试图将 41 个字节写入 ...29a7,因此超过 运行结构体。您需要为协议所需的特定结构分配内存。可能你的 addr 成员应该是一个指针,你应该为它分配内存,将指针设置为指向该内存,并调整你的其他代码。