Always fun, cmsg structures. Anyway, I'll try to find some info on
this (maybe Stevens has something to say here).

But your experiments indicate that various Unixes do not agree, so
it's probably better to avoid sending multiple cmsg structures. Or
there is a subtle error in the way you build your messages...

        -Otto

On Tue, Apr 14, 2015 at 09:26:25PM -0400, William Orr wrote:

> 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
> 
> 
> 
> 
> 


Reply via email to