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 d157927807 was not intended to bring in
functional API changes.

Provide tests for both cases.

PR:			286692
Fixes:			d157927807
This commit is contained in:
Gleb Smirnoff 2025-05-20 17:30:57 -07:00
parent d5566d7556
commit eafe5967ac
2 changed files with 61 additions and 3 deletions

View file

@ -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);

View file

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