cpointerssegmentation-faultmemory-corruption

Unexpected C behavior involving pointers


I'm tryng to let a char* variable point to a malloc'd string but something really wrong is happening and i'm dealing with a segmentation fault.

That's the involved portion of my code, I've tried to give an executable example but using AF_UNIX socket I'm afraid it won't be easly testable.

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

Expectd output:

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

Actual output:

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

The only possible explanation I can think is that I corrupted the stack somehow, if other resources are needed please let me know and i'll try to provide them as soon as possible

Additional information:

Tryn' to printf("%s\n", *result) just before the return 0 I'm getting the expected string.

The server uses the same connection_* function to comunicate with the client.

I'm on on RHEL8.1 and i'm compiling it with gcc 8.3.1


Solution

  • Your connection_t merely has a plain struct sockaddr addr member, which is just the base structure. The actual data size needed varies based on the protocol. When I run your code, con is allocated at …29a0 and has 24 bytes, but the strncpy attempts to write 41 bytes to …29a7, thus overrunning the structure. You need to allocate memory for the specific structure needed by the protocol. Likely your addr member should be a pointer, and you should allocate memory for it, set the pointer to point to that memory, and adjust your other code.