aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGleb Smirnoff <glebius@FreeBSD.org>2025-05-21 00:30:57 +0000
committerGleb Smirnoff <glebius@FreeBSD.org>2025-05-21 02:54:51 +0000
commiteafe5967ac558de142d91660e18e9238289890e3 (patch)
tree93447dda75706c86766f17c664dc0da9906eb940
parentd5566d755694b0efeef9b0e0ff839ee5b3ef43bd (diff)
unix: fix EVFILT_WRITE when peer close(2)s and shutdown(2)s
For the close(2) case restore reporting the event with EV_EOF set. This fixes bug 286692. For the shutdown(2) case restore original behavior, but leave comment that we may want to change that. The d15792780760 was not intended to bring in functional API changes. Provide tests for both cases. PR: 286692 Fixes: d15792780760ef94647af9b377b5f0a80e1826bc
-rw-r--r--sys/kern/uipc_usrreq.c16
-rw-r--r--tests/sys/kern/unix_stream.c48
2 files changed, 61 insertions, 3 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 0836652b6a24..164030eec7ab 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -1767,16 +1767,26 @@ uipc_filt_sowrite(struct knote *kn, long hint)
struct socket *so = kn->kn_fp->f_data, *so2;
struct unpcb *unp = sotounpcb(so), *unp2 = unp->unp_conn;
- if (SOLISTENING(so) || unp2 == NULL)
+ if (SOLISTENING(so))
return (0);
+ if (unp2 == NULL) {
+ if (so->so_state & SS_ISDISCONNECTED) {
+ kn->kn_flags |= EV_EOF;
+ kn->kn_fflags = so->so_error;
+ return (1);
+ } else
+ return (0);
+ }
+
so2 = unp2->unp_socket;
SOCK_RECVBUF_LOCK_ASSERT(so2);
kn->kn_data = uipc_stream_sbspace(&so2->so_rcv);
if (so2->so_rcv.sb_state & SBS_CANTRCVMORE) {
- kn->kn_flags |= EV_EOF;
- kn->kn_fflags = so->so_error;
+ /*
+ * XXXGL: maybe kn->kn_flags |= EV_EOF ?
+ */
return (1);
} else if (kn->kn_sfflags & NOTE_LOWAT)
return (kn->kn_data >= kn->kn_sdata);
diff --git a/tests/sys/kern/unix_stream.c b/tests/sys/kern/unix_stream.c
index 7aedf462dcce..9f750967ebf8 100644
--- a/tests/sys/kern/unix_stream.c
+++ b/tests/sys/kern/unix_stream.c
@@ -279,6 +279,52 @@ ATF_TC_BODY(unconnected_writability, tc)
close(s);
}
+ATF_TC_WITHOUT_HEAD(peerclosed_writability);
+ATF_TC_BODY(peerclosed_writability, tc)
+{
+ struct kevent kev;
+ int sv[2], kq;
+
+ do_socketpair(sv);
+ close(sv[1]);
+
+ check_writable_select(sv[0], 1, false);
+ check_writable_poll(sv[0], 1, false);
+
+ ATF_REQUIRE(kq = kqueue());
+ EV_SET(&kev, sv[0], EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &kev, 1, &kev, 1, NULL) == 1);
+ ATF_REQUIRE(kev.ident == (uintptr_t)sv[0] &&
+ kev.filter == EVFILT_WRITE &&
+ kev.flags == EV_EOF);
+
+ close(sv[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(peershutdown_writability);
+ATF_TC_BODY(peershutdown_writability, tc)
+{
+ int sv[2];
+
+ do_socketpair(sv);
+ shutdown(sv[1], SHUT_RD);
+
+ check_writable_select(sv[0], 1, false);
+ check_writable_poll(sv[0], 1, false);
+ /*
+ * XXXGL: historically unix(4) sockets were not reporting peer's
+ * shutdown(SHUT_RD) as our EV_EOF. The kevent(2) manual page says
+ * "filter will set EV_EOF when the reader disconnects", which is hard
+ * to interpret unambigously. For now leave the historic behavior,
+ * but we may want to change that in uipc_usrreq.c:uipc_filt_sowrite(),
+ * and then this test will look like the peerclosed_writability test.
+ */
+ check_writable_kevent(sv[0], 1, false);
+
+ close(sv[0]);
+ close(sv[1]);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, getpeereid);
@@ -288,6 +334,8 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, full_writability_select);
ATF_TP_ADD_TC(tp, full_writability_poll);
ATF_TP_ADD_TC(tp, full_writability_kevent);
+ ATF_TP_ADD_TC(tp, peerclosed_writability);
+ ATF_TP_ADD_TC(tp, peershutdown_writability);
return atf_no_error();
}