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());
}