c++linuxsocketssctp

SCTP recvmsg returns EFAULT (Bad Address)


I'm trying to build a very simple and basic STCP one-to-many server for testing purposes, but for some reason, the socket seems to be able to receive only one message. After receiving one message, each subsequent call to recvmsg returns -1 with the errno being EFAULT. This makes absolutely no sense to me, so maybe you can tell me what I'm not seeing here. EFAULT is supposed to be returned when

EFAULT
The receive buffer pointer(s) point outside the process's address space.

which is definitely not the case.

#include <ctype.h>
#include <cstring>
#include <stdio.h>
#include <iostream>
#include <unistd.h>
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 

int main(int argc, char** argv) {
    struct sockaddr_in bind_address;
    bind_address.sin_family = AF_INET;
    bind_address.sin_port = htons(51338);
    bind_address.sin_addr.s_addr = INADDR_ANY;

    int sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
    if (sock == -1) {
        perror("Error while creating socket");
        return 1;
    }

    int rv = bind(sock, (struct sockaddr*)(&bind_address), sizeof(bind_address));
    if (rv != 0 ) {
        perror("Error while binding socket");
        return 1;
    }

    rv = listen(sock, 1);
    if (rv != 0 ) {
        perror("Error while listening");
        return 1;
    }

    struct iovec iov[1];
    iov[0].iov_base = malloc(1500);
    iov[0].iov_len = 1500;

    struct sockaddr_in sin = {};
    char control[1000] = { 0 };

    struct msghdr mhdr;
    mhdr.msg_iov = iov;
    mhdr.msg_iovlen = sizeof(iov);
    mhdr.msg_name = &sin;
    mhdr.msg_namelen = sizeof(sin);
    mhdr.msg_control = &control;
    mhdr.msg_controllen = sizeof(control);

    while (true) {
        std::memset(iov[0].iov_base, 0, iov[0].iov_len);
        std::memset(&sin, 0, sizeof(sin));

        int recv_bytes = recvmsg(sock, &mhdr, 0);

        if (recv_bytes == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                continue;
            } else {
                perror("Err");
            }
            break;
        }

        std::cout << "Received message: " << std::endl;
        std::cout << (char*)iov[0].iov_base << std::endl;
    }

    return 0;
}

Solution

  • The error is most likely because the first call to recvmsg() overwrites some data on the stack, causing one of the pointers inside mhdr to be invalid. And this is probably because of this line:

    mhdr.msg_iovlen = sizeof(iov);
    

    This should not contain the size of the array iov in bytes, but just the number of elements in that array, so:

    mhdr.msg_iovlen = sizeof(iov) / sizeof(*iov); // which equals 1 in this case