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
signature.asc
Description: OpenPGP digital signature