diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c index 6c9843b76595..7267fe931b43 100644 --- a/tests/sys/kern/unix_passfd_test.c +++ b/tests/sys/kern/unix_passfd_test.c @@ -118,6 +118,20 @@ getnfds(void) return (n); } +static void +putfds(char *buf, int fd, int nfds) +{ + struct cmsghdr *cm; + int *fdp, i; + + cm = (struct cmsghdr *)buf; + cm->cmsg_len = CMSG_LEN(nfds * sizeof(int)); + cm->cmsg_level = SOL_SOCKET; + cm->cmsg_type = SCM_RIGHTS; + for (fdp = (int *)CMSG_DATA(cm), i = 0; i < nfds; i++) + *fdp++ = fd; +} + static void samefile(struct stat *sb1, struct stat *sb2) { @@ -131,7 +145,6 @@ sendfd_payload(int sockfd, int send_fd, void *payload, size_t paylen) { struct iovec iovec; char message[CMSG_SPACE(sizeof(int))]; - struct cmsghdr *cmsghdr; struct msghdr msghdr; ssize_t len; @@ -147,13 +160,8 @@ sendfd_payload(int sockfd, int send_fd, void *payload, size_t paylen) msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; - cmsghdr = (struct cmsghdr *)(void *)message; - cmsghdr->cmsg_len = CMSG_LEN(sizeof(int)); - cmsghdr->cmsg_level = SOL_SOCKET; - cmsghdr->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsghdr), &send_fd, sizeof(int)); - - len = sendmsg(sockfd, &msghdr, MSG_DONTWAIT); + putfds(message, send_fd, 1); + len = sendmsg(sockfd, &msghdr, 0); ATF_REQUIRE_MSG(len != -1, "sendmsg failed: %s", strerror(errno)); return ((size_t)len); } @@ -185,20 +193,22 @@ localcreds(int sockfd) } static void -recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen) +recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen, + size_t cmsgsz) { struct cmsghdr *cmsghdr; - char message[CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + - CMSG_SPACE(sizeof(int))]; struct msghdr msghdr; struct iovec iovec; + char *message; ssize_t len; bool foundcreds; bzero(&msghdr, sizeof(msghdr)); + message = malloc(cmsgsz); + ATF_REQUIRE(message != NULL); msghdr.msg_control = message; - msghdr.msg_controllen = sizeof(message); + msghdr.msg_controllen = cmsgsz; iovec.iov_base = buf; iovec.iov_len = buflen; @@ -237,7 +247,8 @@ recvfd(int sockfd, int *recv_fd) { char ch = 0; - recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch)); + recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch), + CMSG_SPACE(sizeof(int))); } /* @@ -418,65 +429,159 @@ ATF_TC_BODY(rights_creds_payload, tc) len = sendfd_payload(fd[0], putfd, buf, sendspace); ATF_REQUIRE_MSG(len < sendspace, "sendmsg: %zu bytes sent", len); - recvfd_payload(fd[1], &getfd, buf, len); + recvfd_payload(fd[1], &getfd, buf, len, + CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(int))); close(putfd); close(getfd); closesocketpair(fd); } +static void +send_cmsg(int sockfd, void *cmsg, size_t cmsgsz) +{ + struct iovec iov; + struct msghdr msghdr; + ssize_t len; + char ch; + + ch = 0; + bzero(&msghdr, sizeof(msghdr)); + + iov.iov_base = &ch; + iov.iov_len = sizeof(ch); + msghdr.msg_control = cmsg; + msghdr.msg_controllen = cmsgsz; + msghdr.msg_iov = &iov; + msghdr.msg_iovlen = 1; + + len = sendmsg(sockfd, &msghdr, 0); + ATF_REQUIRE_MSG(len != -1, + "sendmsg failed: %s", strerror(errno)); + ATF_REQUIRE_MSG(len == sizeof(ch), + "sendmsg: %zd bytes sent; expected %zu", len, sizeof(ch)); +} + +static void +recv_cmsg(int sockfd, char *cmsg, size_t cmsgsz, int flags) +{ + struct iovec iov; + struct msghdr msghdr; + ssize_t len; + char ch; + + ch = 0; + bzero(&msghdr, sizeof(msghdr)); + + iov.iov_base = &ch; + iov.iov_len = sizeof(ch); + msghdr.msg_control = cmsg; + msghdr.msg_controllen = cmsgsz; + msghdr.msg_iov = &iov; + msghdr.msg_iovlen = 1; + + len = recvmsg(sockfd, &msghdr, 0); + ATF_REQUIRE_MSG(len != -1, + "recvmsg failed: %s", strerror(errno)); + ATF_REQUIRE_MSG(len == sizeof(ch), + "recvmsg: %zd bytes received; expected %zu", len, sizeof(ch)); + ATF_REQUIRE_MSG((msghdr.msg_flags & flags) == flags, + "recvmsg: got flags %#x; expected %#x", msghdr.msg_flags, flags); +} + /* - * Test for PR 131876. Receiver uses a control message buffer that is too + * Test for PR 131876. Receiver uses a control message buffer that is too * small for the incoming SCM_RIGHTS message, so the message is truncated. * The kernel must not leak the copied right into the receiver's namespace. */ ATF_TC_WITHOUT_HEAD(truncated_rights); ATF_TC_BODY(truncated_rights, tc) { - struct iovec iovec; - struct msghdr msghdr; - char buf[16], message[CMSG_SPACE(0)]; - ssize_t len; - int fd[2], nfds, putfd; + char *message; + int fd[2], nfds, putfd, rc; - atf_tc_expect_fail("PR 131876: " - "FD leak when 'control' message is truncated"); - - memset(buf, 42, sizeof(buf)); domainsocketpair(fd); devnull(&putfd); nfds = getnfds(); - len = sendfd_payload(fd[0], putfd, buf, sizeof(buf)); - ATF_REQUIRE_MSG(len == sizeof(buf), - "sendmsg: %zd bytes sent; expected %zu; %s", len, sizeof(buf), - strerror(errno)); - - bzero(&msghdr, sizeof(msghdr)); - bzero(message, sizeof(message)); - - iovec.iov_base = buf; - iovec.iov_len = sizeof(buf); - msghdr.msg_control = message; - msghdr.msg_controllen = sizeof(message); - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - - len = recvmsg(fd[1], &msghdr, 0); - ATF_REQUIRE_MSG(len != -1, "recvmsg failed: %s", strerror(errno)); - ATF_REQUIRE_MSG((size_t)len == sizeof(buf), - "recvmsg: %zd bytes received; expected %zd", len, sizeof(buf)); - for (size_t i = 0; i < sizeof(buf); i++) - ATF_REQUIRE_MSG(buf[i] == 42, "unexpected buffer contents"); - - ATF_REQUIRE_MSG((msghdr.msg_flags & MSG_CTRUNC) != 0, - "MSG_CTRUNC not set after truncation"); + /* + * Case 1: Send a single descriptor and truncate the message. + */ + message = malloc(CMSG_SPACE(sizeof(int))); + ATF_REQUIRE(message != NULL); + putfds(message, putfd, 1); + send_cmsg(fd[0], message, CMSG_LEN(sizeof(int))); + recv_cmsg(fd[1], message, CMSG_LEN(0), MSG_CTRUNC); ATF_REQUIRE(getnfds() == nfds); + free(message); + + /* + * Case 2a: Send two descriptors in separate messages, and truncate at + * the boundary between the two messages. We should still + * receive the first message. + */ + message = malloc(CMSG_SPACE(sizeof(int)) * 2); + ATF_REQUIRE(message != NULL); + putfds(message, putfd, 1); + putfds(message + CMSG_SPACE(sizeof(int)), putfd, 1); + send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int)) * 2); + recv_cmsg(fd[1], message, CMSG_SPACE(sizeof(int)), MSG_CTRUNC); + rc = close(*(int *)CMSG_DATA(message)); + ATF_REQUIRE_MSG(rc == 0, "close failed: %s", strerror(errno)); + ATF_REQUIRE(getnfds() == nfds); + free(message); + + /* + * Case 2b: Send two descriptors in separate messages, and truncate + * before the end of the first message. + */ + message = malloc(CMSG_SPACE(sizeof(int)) * 2); + ATF_REQUIRE(message != NULL); + putfds(message, putfd, 1); + putfds(message + CMSG_SPACE(sizeof(int)), putfd, 1); + send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int)) * 2); + recv_cmsg(fd[1], message, CMSG_SPACE(0), MSG_CTRUNC); + ATF_REQUIRE(getnfds() == nfds); + free(message); + + /* + * Case 2c: Send two descriptors in separate messages, and truncate + * after the end of the first message. We should still + * receive the first message. + */ + message = malloc(CMSG_SPACE(sizeof(int)) * 2); + ATF_REQUIRE(message != NULL); + putfds(message, putfd, 1); + putfds(message + CMSG_SPACE(sizeof(int)), putfd, 1); + send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int)) * 2); + recv_cmsg(fd[1], message, CMSG_SPACE(sizeof(int)) + CMSG_SPACE(0), + MSG_CTRUNC); + rc = close(*(int *)CMSG_DATA(message)); + ATF_REQUIRE_MSG(rc == 0, "close failed: %s", strerror(errno)); + ATF_REQUIRE(getnfds() == nfds); + free(message); + + /* + * Case 3: Send three descriptors in the same message, and leave space + * only for the first when receiving the message. + */ + message = malloc(CMSG_SPACE(sizeof(int) * 3)); + ATF_REQUIRE(message != NULL); + putfds(message, putfd, 3); + send_cmsg(fd[0], message, CMSG_SPACE(sizeof(int) * 3)); + recv_cmsg(fd[1], message, CMSG_SPACE(sizeof(int)), MSG_CTRUNC); + ATF_REQUIRE(getnfds() == nfds); + free(message); close(putfd); closesocketpair(fd); } +/* + * Ensure that an attempt to copy a SCM_RIGHTS message to the recipient + * fails. In this case the kernel must dispose of the externalized rights + * rather than leaking them into the recipient's file descriptor table. + */ ATF_TC_WITHOUT_HEAD(copyout_rights_error); ATF_TC_BODY(copyout_rights_error, tc) { @@ -486,9 +591,6 @@ ATF_TC_BODY(copyout_rights_error, tc) ssize_t len; int fd[2], error, nfds, putfd; - atf_tc_expect_fail("PR 131876: " - "FD leak when copyout of rights returns an error"); - memset(buf, 0, sizeof(buf)); domainsocketpair(fd); devnull(&putfd);