git: 1e68b8d9a90f - main - tests/unix_passfd: test that control mixed with data creates records
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 08 Feb 2024 17:01:05 UTC
The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=1e68b8d9a90f3ddf5d0766ea3b5a6c6ec9088b2f commit 1e68b8d9a90f3ddf5d0766ea3b5a6c6ec9088b2f Author: Gleb Smirnoff <glebius@FreeBSD.org> AuthorDate: 2024-02-08 17:00:23 +0000 Commit: Gleb Smirnoff <glebius@FreeBSD.org> CommitDate: 2024-02-08 17:00:41 +0000 tests/unix_passfd: test that control mixed with data creates records If socket has data interleaved with control it would never allow to read two pieces of data, neither two pieces of control with one recvmsg(2). In other words, presence of control makes a SOCK_STREAM socket behave like SOCK_SEQPACKET, where control marks the records. This is not a documented or specified behavior, but this is how it worked always for BSD sockets. If you look closer at it, this actually makes a lot of sense, as if it were the opposite both the kernel code and an application code would become way more complex. The change made recvfd_payload() to return received length and requires caller to do ATF_REQUIRE() itself. This required a small change to existing test rights_creds_payload. It also refactors a bit f28532a0f363, pushing two identical calls out of TEST_PROTO ifdef. Reviwed by: markj Differential Revision: https://reviews.freebsd.org/D43724 --- tests/sys/kern/unix_passfd_test.c | 56 +++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c index 67171e62c963..143ccd098583 100644 --- a/tests/sys/kern/unix_passfd_test.c +++ b/tests/sys/kern/unix_passfd_test.c @@ -204,7 +204,7 @@ localcreds(int sockfd) return (val != 0); } -static void +static ssize_t recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen, size_t cmsgsz, int recvmsg_flags) { @@ -230,8 +230,6 @@ recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen, len = recvmsg(sockfd, &msghdr, recvmsg_flags); ATF_REQUIRE_MSG(len != -1, "recvmsg failed: %s", strerror(errno)); - ATF_REQUIRE_MSG((size_t)len == buflen, - "recvmsg: %zd bytes received; expected %zd", len, buflen); cmsghdr = CMSG_FIRSTHDR(&msghdr); ATF_REQUIRE_MSG(cmsghdr != NULL, @@ -254,15 +252,20 @@ recvfd_payload(int sockfd, int *recv_fd, void *buf, size_t buflen, "recvmsg: expected credentials were not received"); ATF_REQUIRE_MSG((msghdr.msg_flags & MSG_TRUNC) == 0, "recvmsg: MSG_TRUNC is set while buffer is sufficient"); + + return (len); } static void recvfd(int sockfd, int *recv_fd, int flags) { + ssize_t len; char ch = 0; - recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch), + len = recvfd_payload(sockfd, recv_fd, &ch, sizeof(ch), CMSG_SPACE(sizeof(int)), flags); + ATF_REQUIRE_MSG((size_t)len == sizeof(ch), + "recvmsg: %zd bytes received; expected %zd", len, sizeof(ch)); } #if TEST_PROTO == SOCK_STREAM @@ -632,7 +635,7 @@ ATF_TC_BODY(rights_creds_payload, tc) { const int on = 1; u_long sendspace; - ssize_t len; + ssize_t len, rlen; void *buf; int fd[2], getfd, putfd, rc; @@ -651,20 +654,19 @@ ATF_TC_BODY(rights_creds_payload, tc) strerror(errno)); len = sendfd_payload(fd[0], putfd, buf, sendspace); -#if TEST_PROTO == SOCK_STREAM ATF_REQUIRE_MSG(len != -1 , "sendmsg failed: %s", strerror(errno)); +#if TEST_PROTO == SOCK_STREAM ATF_REQUIRE_MSG((size_t)len < sendspace, "sendmsg: %zd bytes sent", len); - recvfd_payload(fd[1], &getfd, buf, len, - CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(int)), 0); #endif #if TEST_PROTO == SOCK_DGRAM - ATF_REQUIRE_MSG(len != -1 , "sendmsg failed: %s", strerror(errno)); ATF_REQUIRE_MSG((size_t)len == sendspace, "sendmsg: %zd bytes sent", len); - recvfd_payload(fd[1], &getfd, buf, len, - CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(int)), 0); #endif + rlen = recvfd_payload(fd[1], &getfd, buf, len, + CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(int)), 0); + ATF_REQUIRE_MSG(rlen == len, + "recvmsg: %zd bytes received; expected %zd", rlen, len); close(putfd); close(getfd); @@ -945,6 +947,37 @@ ATF_TC_BODY(empty_rights_message, tc) (void)close(putfd); } +/* + * Check that sending control creates records in a stream socket, making it + * behave like a seqpacket socket. If we stack several control+data writes + * on a stream socket, we won't be able to read them all at once, even if we + * provide a buffer large enough to receive all at once. + * + * XXXGL: adding MSG_WAITALL to the recvmsg() flags will make this test stuck. + */ +ATF_TC_WITHOUT_HEAD(control_creates_records); +ATF_TC_BODY(control_creates_records, tc) +{ + int fd[2], putfd, getfd; + char buf[2]; + ssize_t rlen; + + domainsocketpair(fd); + tempfile(&putfd); + + for (int i = 1; i <= 2; i++) + ATF_REQUIRE(sendfd_payload(fd[0], putfd, buf, 1) == 1); + ATF_REQUIRE(close(putfd) == 0); + for (int i = 1; i <= 2; i++) { + rlen = recvfd_payload(fd[1], &getfd, buf, 2, + CMSG_SPACE(sizeof(int)) * 2, 0); + ATF_REQUIRE_MSG(rlen == 1, + "recvmsg: %zd bytes received; expected 1", rlen); + ATF_REQUIRE(close(getfd) == 0); + } + closesocketpair(fd); +} + ATF_TP_ADD_TCS(tp) { @@ -963,6 +996,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, truncated_rights); ATF_TP_ADD_TC(tp, copyout_rights_error); ATF_TP_ADD_TC(tp, empty_rights_message); + ATF_TP_ADD_TC(tp, control_creates_records); return (atf_no_error()); }