--- IO-FDPass/FDPass.xs 2016/07/11 23:00:00 1.7 +++ IO-FDPass/FDPass.xs 2022/09/06 10:49:13 1.9 @@ -191,22 +191,47 @@ msg.msg_controllen = CMSG_SPACE (sizeof (int)); if (recvmsg (socket, &msg, 0) <= 0) - return -1; + { + free (buf); + return -1; + } int fd = -1; - errno = EDOM; struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg); - if (data == 0 - && cmsg - && cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS - && cmsg->cmsg_len >= CMSG_LEN (sizeof (int))) - fd = *(int *)CMSG_DATA (cmsg); + if (cmsg) + { + // some operating systems (i.e. osx) allow msg->cmsg_len to be larger than + // msg.msg_controllen, so limit the size here + if (cmsg->cmsg_len > CMSG_SPACE (sizeof (int))) + cmsg->cmsg_len = CMSG_SPACE (sizeof (int)); + + if ( cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS + && cmsg->cmsg_len >= CMSG_LEN (sizeof (int))) + { + // close any extra fd's that might have been passed. + // this does not work around osx/freebsad bugs where a malicious sender + // can send usw more fds than we can receive, leaking the extra fds, + // which must be fixed in the kernel, really. + for (fd = 1; cmsg->cmsg_len >= CMSG_LEN (sizeof (int) * (fd + 1)); ++fd) + close (((int *)CMSG_DATA (cmsg))[fd]); + + fd = *(int *)CMSG_DATA (cmsg); + } + } free (buf); + if (data != 0) + { + close (fd); + fd = -1; + } + + errno = EDOM; + return fd; #endif }