git: 6b90209aaf80 - main - tests: ensure that unix/stream raises POLLIN after a shutdown(2)

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Sat, 31 May 2025 00:04:57 UTC
The branch main has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=6b90209aaf809204c62e2a252c931e7240f60374

commit 6b90209aaf809204c62e2a252c931e7240f60374
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2025-05-31 00:04:33 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2025-05-31 00:04:33 +0000

    tests: ensure that unix/stream raises POLLIN after a shutdown(2)
    
    This tests for the bug fixed in
    6ac71c4a52348 ("unix/stream: fix poll on a peer shutdown(2)ed socket"),
    where-in the remote side has shutdown writes on their side and the other
    end fails to return from select(2)/poll(2) because we weren't surfacing
    it as readable.
    
    Reviewed by:    adrian, kib
    Differential Revision:  https://reviews.freebsd.org/D50602
---
 tests/sys/kern/unix_stream.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/tests/sys/kern/unix_stream.c b/tests/sys/kern/unix_stream.c
index 9f750967ebf8..f8ba288308bd 100644
--- a/tests/sys/kern/unix_stream.c
+++ b/tests/sys/kern/unix_stream.c
@@ -28,6 +28,7 @@
 #include <sys/cdefs.h>
 #include <sys/socket.h>
 #include <sys/event.h>
+#include <sys/select.h>
 #include <sys/sysctl.h>
 #include <sys/un.h>
 #include <errno.h>
@@ -101,6 +102,20 @@ ATF_TC_BODY(send_0, tc)
 	close(sv[1]);
 }
 
+static void
+check_readable_select(int fd, int expect, bool timeout)
+{
+	fd_set rdfds;
+	int nfds;
+
+	FD_ZERO(&rdfds);
+	FD_SET(fd, &rdfds);
+	nfds = select(fd + 1, &rdfds, NULL, NULL, timeout ?
+	    &(struct timeval){.tv_usec = 1000} : NULL);
+	ATF_REQUIRE_MSG(nfds == expect,
+	    "select() returns %d errno %d", nfds, errno);
+}
+
 static void
 check_writable_select(int fd, int expect, bool timeout)
 {
@@ -115,6 +130,21 @@ check_writable_select(int fd, int expect, bool timeout)
 	    "select() returns %d errno %d", nfds, errno);
 }
 
+static void
+check_readable_poll(int fd, int expect, bool timeout)
+{
+	struct pollfd pfd[1];
+	int nfds;
+
+	pfd[0] = (struct pollfd){
+		.fd = fd,
+		.events = POLLIN | POLLRDNORM,
+	};
+	nfds = poll(pfd, 1, timeout ? 1 : INFTIM);
+	ATF_REQUIRE_MSG(nfds == expect,
+	    "poll() returns %d errno %d", nfds, errno);
+}
+
 static void
 check_writable_poll(int fd, int expect, bool timeout)
 {
@@ -325,6 +355,31 @@ ATF_TC_BODY(peershutdown_writability, tc)
 	close(sv[1]);
 }
 
+ATF_TC_WITHOUT_HEAD(peershutdown_readability);
+ATF_TC_BODY(peershutdown_readability, tc)
+{
+	ssize_t readsz;
+	int sv[2];
+	char c;
+
+	do_socketpair(sv);
+	shutdown(sv[1], SHUT_WR);
+
+	/*
+	 * The other side should flag as readable in select(2) to allow it to
+	 * read(2) and observe EOF.  Ensure that both poll(2) and select(2)
+	 * are consistent here.
+	 */
+	check_readable_select(sv[0], 1, false);
+	check_readable_poll(sv[0], 1, false);
+
+	readsz = read(sv[0], &c, sizeof(c));
+	ATF_REQUIRE_INTEQ(0, readsz);
+
+	close(sv[0]);
+	close(sv[1]);
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 	ATF_TP_ADD_TC(tp, getpeereid);
@@ -336,6 +391,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, full_writability_kevent);
 	ATF_TP_ADD_TC(tp, peerclosed_writability);
 	ATF_TP_ADD_TC(tp, peershutdown_writability);
+	ATF_TP_ADD_TC(tp, peershutdown_readability);
 
 	return atf_no_error();
 }