Hey,

I was debugging a few CPython test failures yesterday, and I noticed
that attaching multiple cmsg structures causes unp_internalize to return
EINVAL.

I've looked in unix(4) and sendmsg(2), and this caveat isn't documented
anywhere.

I looked at other OSes, and Linux supports this, FreeBSD fails in
interesting ways and OS X returns E2BIG.

Is this behavior intentional, and the documentation is missing this
failure mode? Or is the behavior unintentional? I'm happy to submit a
patch for either, I just want to know which behavior is intended.

For reference, the code that returns EINVAL follows:

int
unp_internalize(struct mbuf *control, struct proc *p)
{
        struct filedesc *fdp = p->p_fd;
        struct cmsghdr *cm = mtod(control, struct cmsghdr *);
        struct file **rp, *fp;
        int i, error;
        int nfds, *ip, fd, neededspace;

        /*
         * Check for two potential msg_controllen values because
         * IETF stuck their nose in a place it does not belong.
         */
        if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
            !(cm->cmsg_len == control->m_len ||
            control->m_len == CMSG_ALIGN(cm->cmsg_len)))
                return (EINVAL);
...

My super-awful test, also follows:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <string.h>

void
child(int sock)
{
        struct msghdr msg;
        memset(&msg, 0, sizeof(msg));
        recvmsg(sock, &msg, 0);

        printf("controllen: %zu\n", msg.msg_controllen);
        printf("control: %p\n", msg.msg_control);
}

void
parent(int sock)
{
        int fds[] = { -1, -1 };
        struct msghdr msg;
        struct cmsghdr  *cmsg;
        union {
                struct cmsghdr hdr;
                unsigned char    buf[2 * CMSG_SPACE(sizeof(int))];
        } cmsgbuf;
        char sfn[30];

        memset(&msg, 0, sizeof(msg));
        for (int i = 0; i < sizeof(fds); i++) {
                (void)strlcpy(sfn, "/tmp/worrtest.XXXXXX", sizeof(sfn));
                if ((fds[i] = mkstemp(sfn)) == -1) {
                        err(1, "mkstemp");
                }
        }

        msg.msg_control = &cmsgbuf.buf;
        msg.msg_controllen = sizeof(cmsgbuf.buf);

        cmsg = CMSG_FIRSTHDR(&msg);
        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
        cmsg->cmsg_level = SOL_SOCKET;
        cmsg->cmsg_type = SCM_RIGHTS;
        *(int *)CMSG_DATA(cmsg) = fds[0];

        cmsg = CMSG_NXTHDR(&msg, cmsg);
        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
        cmsg->cmsg_level = SOL_SOCKET;
        cmsg->cmsg_type = SCM_RIGHTS;
        *(int *)CMSG_DATA(cmsg) = fds[1];

        if (sendmsg(sock, &msg, 10240) == -1)
                err(1, "sendmsg");
}

int
main(void)
{
        int sock[] = {-1, -1};

        if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) == -1)
                err(1, "socket");

        switch (fork()) {
                case 0:
                        child(sock[0]);
                        exit(0);
                case -1:
                        err(1, "fork");
                default:
                        parent(sock[1]);
                        exit(0);
        }
}


Thanks,
William Orr





Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to