From nobody Fri Jun 17 19:24:00 2022 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id D2D1A8435B2; Fri, 17 Jun 2022 19:24:00 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4LPpr05Fbnz3Bs7; Fri, 17 Jun 2022 19:24:00 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1655493840; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=2ab0FLseO7c/LzOx4zJh/eBhVHq1VbDAzIj7GwuiBZQ=; b=h0sWYhGpdphMXu/ZeG4jYI1m6NxZCtlocKIYC8u5vGFvyDfd5VhTwAGAsltktB5hQu7VJI h6I2HiyuZrWtCyfVIqbdG4WaiaJAo8puUZKAad0m0oU9VCrToGpDekMgaFpA+gqBzqhWBu devTRVrAfvDXt5DksKqpiAtLfy+W9TScL8z+qNLpbaH5ml5dQHw9nl2pHv2mZDsIpM0dVC uAHLAjLWHGdwysbu6gPkSbmkEJSclpmmJZX1iBI7e7sDu9DBQgI/PzS9or5L68nwShkKdd XHiQ7w8MgyPBakBfA2A063lc+XJRYmJIauz/ETv+4EDAX9wTzJ81qv3N1KhvNg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 8915E2586F; Fri, 17 Jun 2022 19:24:00 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 25HJO041095858; Fri, 17 Jun 2022 19:24:00 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 25HJO0cf095857; Fri, 17 Jun 2022 19:24:00 GMT (envelope-from git) Date: Fri, 17 Jun 2022 19:24:00 GMT Message-Id: <202206171924.25HJO0cf095857@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Dmitry Chagin Subject: git: 5fac6f16877f - stable/13 - poll(2): Add POLLRDHUP. List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: dchagin X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: 5fac6f16877f6c0c5bb9de706b552fd405006690 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1655493840; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=2ab0FLseO7c/LzOx4zJh/eBhVHq1VbDAzIj7GwuiBZQ=; b=D80MIr5TOzo1fxbNUOrVEo/ZcC12hMScc2uDUMBTa1SFUpbIZZcSrgpKjQfb8a3PD/Tghh JDezDIColVarCuSm5753sAVHdv1B3xp4VPut0WmXgStsArkpB8jRFG587UVgZHfmXSWHaI 2PcK327Iwfg01ftx2I57IZDILRBGQUnoNHT8O9JU7kp7Sb5YFyrmcxHzHhdcW4Ch3hdWdS vX8QsIu2DKBt+tNUHz2qGUJ0v4hSG1qEQUD+YVxbhWRrr/zBiG2ToWQkiUdokDvkEgWw0p C1MI2dyT2HY0xL4s1kCbD8h9Bmkv6NG0GMrCzSQLckHzhdmBkOwuei2Q61CX/Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1655493840; a=rsa-sha256; cv=none; b=o9PgMO7I6wTxNw9HYXhfYjwanCHLllo3k2rgQicQcFH62LJ9gjaSAW+eQzKb9MiN71+fk4 bD4qCNukr/Q2YOnsqJ1vBeEMFjnBoxSI2umMjUGqH/LhU28hzHYPfg9qR6sNA6YLg9USeJ 13G8CIW3gTjtql694H6z3XvkFj9bYggrSIyzTR4ofzVWx39GlyzHMaV8aaWAW36mC8uFeg 9PuxGW8KdiT6xLMb5zut4JTfQBfWdI4ANAz2/Mfjmbts7JxLxpbjKrlffP7SAaSq/IHMi5 Yjrj6v9wVLKCKfgVJME76w4YxypXXGBqu85M/HufiAxsojj3RxIUqJMJ0ETdSw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by dchagin: URL: https://cgit.FreeBSD.org/src/commit/?id=5fac6f16877f6c0c5bb9de706b552fd405006690 commit 5fac6f16877f6c0c5bb9de706b552fd405006690 Author: Thomas Munro AuthorDate: 2021-04-28 09:31:38 +0000 Commit: Dmitry Chagin CommitDate: 2022-06-17 19:22:13 +0000 poll(2): Add POLLRDHUP. Teach poll(2) to support Linux-style POLLRDHUP events for sockets, if requested. Triggered when the remote peer shuts down writing or closes its end. Reviewed by: kib MFC after: 1 month Differential Revision: https://reviews.freebsd.org/D29757 (cherry picked from commit 3aaaa2efde896e19d229ee2cf09fe7e6ab0fbf6e) --- lib/libc/sys/poll.2 | 14 +++- sys/kern/uipc_socket.c | 4 +- sys/sys/poll.h | 1 + tests/sys/netinet/socket_afinet.c | 141 ++++++++++++++++++++++++++++++++++++++ usr.bin/truss/syscalls.c | 2 +- 5 files changed, 159 insertions(+), 3 deletions(-) diff --git a/lib/libc/sys/poll.2 b/lib/libc/sys/poll.2 index bea4aac82bd3..fec82db08944 100644 --- a/lib/libc/sys/poll.2 +++ b/lib/libc/sys/poll.2 @@ -28,7 +28,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd February 27, 2019 +.Dd April 27, 2021 .Dt POLL 2 .Os .Sh NAME @@ -126,6 +126,15 @@ POLLOUT should never be present in the .Fa revents bitmask at the same time. +.It POLLRDHUP +Remote peer closed connection, or shut down writing. +Unlike +POLLHUP, +POLLRDHUP +must be present in the +.Fa events +bitmask to be reported. +Applies only to stream sockets. .It POLLNVAL The file descriptor is not open, or in capability mode the file descriptor has insufficient rights. @@ -261,6 +270,9 @@ function conforms to The .Fn ppoll is not specified by POSIX. +The +POLLRDHUP +flag is not specified by POSIX, but is compatible with Linux and illumos. .Sh HISTORY The .Fn poll diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index bbcd5dd30320..8f2122c98ef5 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -3581,9 +3581,11 @@ sopoll_generic(struct socket *so, int events, struct ucred *active_cred, revents |= POLLHUP; } } + if (so->so_rcv.sb_state & SBS_CANTRCVMORE) + revents |= events & POLLRDHUP; if (revents == 0) { if (events & - (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) { + (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND | POLLRDHUP)) { selrecord(td, &so->so_rdsel); so->so_rcv.sb_flags |= SB_SEL; } diff --git a/sys/sys/poll.h b/sys/sys/poll.h index 6805704ca39f..02347ed09a13 100644 --- a/sys/sys/poll.h +++ b/sys/sys/poll.h @@ -71,6 +71,7 @@ struct pollfd { #if __BSD_VISIBLE /* General FreeBSD extension (currently only supported for sockets): */ #define POLLINIGNEOF 0x2000 /* like POLLIN, except ignore EOF */ +#define POLLRDHUP 0x4000 /* half shut down */ #endif /* diff --git a/tests/sys/netinet/socket_afinet.c b/tests/sys/netinet/socket_afinet.c index 54585086da23..985d67d83c99 100644 --- a/tests/sys/netinet/socket_afinet.c +++ b/tests/sys/netinet/socket_afinet.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -89,12 +90,152 @@ ATF_TC_BODY(socket_afinet_bind_ok, tc) close(sd); } +ATF_TC_WITHOUT_HEAD(socket_afinet_poll_no_rdhup); +ATF_TC_BODY(socket_afinet_poll_no_rdhup, tc) +{ + int ss, ss2, cs, rc; + struct sockaddr_in sin; + struct pollfd pfd; + int one = 1; + + /* Verify that we don't expose POLLRDHUP if not requested. */ + + /* Server setup. */ + ss = socket(PF_INET, SOCK_STREAM, 0); + ATF_CHECK(ss >= 0); + rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); + ATF_CHECK_EQ(0, rc); + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_port = htons(6666); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin)); + ATF_CHECK_EQ(0, rc); + rc = listen(ss, 1); + ATF_CHECK_EQ(0, rc); + + /* Client connects, server accepts. */ + cs = socket(PF_INET, SOCK_STREAM, 0); + ATF_CHECK(cs >= 0); + rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); + ATF_CHECK_EQ(0, rc); + ss2 = accept(ss, NULL, NULL); + ATF_CHECK(ss2 >= 0); + + /* Server can write, sees only POLLOUT. */ + pfd.fd = ss2; + pfd.events = POLLIN | POLLOUT; + rc = poll(&pfd, 1, 0); + ATF_CHECK_EQ(1, rc); + ATF_CHECK_EQ(POLLOUT, pfd.revents); + + /* Client closes socket! */ + rc = close(cs); + ATF_CHECK_EQ(0, rc); + + /* + * Server now sees POLLIN, but not POLLRDHUP because we didn't ask. + * Need non-zero timeout to wait for the FIN to arrive and trigger the + * socket to become readable. + */ + pfd.fd = ss2; + pfd.events = POLLIN; + rc = poll(&pfd, 1, 60000); + ATF_CHECK_EQ(1, rc); + ATF_CHECK_EQ(POLLIN, pfd.revents); + + close(ss2); + close(ss); +} + +ATF_TC_WITHOUT_HEAD(socket_afinet_poll_rdhup); +ATF_TC_BODY(socket_afinet_poll_rdhup, tc) +{ + int ss, ss2, cs, rc; + struct sockaddr_in sin; + struct pollfd pfd; + char buffer; + int one = 1; + + /* Verify that server sees POLLRDHUP if it asks for it. */ + + /* Server setup. */ + ss = socket(PF_INET, SOCK_STREAM, 0); + ATF_CHECK(ss >= 0); + rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); + ATF_CHECK_EQ(0, rc); + bzero(&sin, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(sin); + sin.sin_port = htons(6666); + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin)); + ATF_CHECK_EQ(0, rc); + rc = listen(ss, 1); + ATF_CHECK_EQ(0, rc); + + /* Client connects, server accepts. */ + cs = socket(PF_INET, SOCK_STREAM, 0); + ATF_CHECK(cs >= 0); + rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); + ATF_CHECK_EQ(0, rc); + ss2 = accept(ss, NULL, NULL); + ATF_CHECK(ss2 >= 0); + + /* Server can write, so sees POLLOUT. */ + pfd.fd = ss2; + pfd.events = POLLIN | POLLOUT | POLLRDHUP; + rc = poll(&pfd, 1, 0); + ATF_CHECK_EQ(1, rc); + ATF_CHECK_EQ(POLLOUT, pfd.revents); + + /* Client writes two bytes, server reads only one of them. */ + rc = write(cs, "xx", 2); + ATF_CHECK_EQ(2, rc); + rc = read(ss2, &buffer, 1); + ATF_CHECK_EQ(1, rc); + + /* Server can read, so sees POLLIN. */ + pfd.fd = ss2; + pfd.events = POLLIN | POLLOUT | POLLRDHUP; + rc = poll(&pfd, 1, 0); + ATF_CHECK_EQ(1, rc); + ATF_CHECK_EQ(POLLIN | POLLOUT, pfd.revents); + + /* Client closes socket! */ + rc = close(cs); + ATF_CHECK_EQ(0, rc); + + /* + * Server sees Linux-style POLLRDHUP. Note that this is the case even + * though one byte of data remains unread. + * + * This races against the delivery of FIN caused by the close() above. + * Sometimes (more likely when run under truss or if another system + * call is added in between) it hits the path where sopoll_generic() + * immediately sees SBS_CANTRCVMORE, and sometimes it sleeps with flag + * SB_SEL so that it's woken up almost immediately and runs again, + * which is why we need a non-zero timeout here. + */ + pfd.fd = ss2; + pfd.events = POLLRDHUP; + rc = poll(&pfd, 1, 60000); + ATF_CHECK_EQ(1, rc); + ATF_CHECK_EQ(POLLRDHUP, pfd.revents); + + close(ss2); + close(ss); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, socket_afinet); ATF_TP_ADD_TC(tp, socket_afinet_bind_zero); ATF_TP_ADD_TC(tp, socket_afinet_bind_ok); + ATF_TP_ADD_TC(tp, socket_afinet_poll_no_rdhup); + ATF_TP_ADD_TC(tp, socket_afinet_poll_rdhup); return atf_no_error(); } diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index a84f64f70ff7..06e984de3fe8 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -736,7 +736,7 @@ struct xlat { static struct xlat poll_flags[] = { X(POLLSTANDARD) X(POLLIN) X(POLLPRI) X(POLLOUT) X(POLLERR) X(POLLHUP) X(POLLNVAL) X(POLLRDNORM) X(POLLRDBAND) - X(POLLWRBAND) X(POLLINIGNEOF) XEND + X(POLLWRBAND) X(POLLINIGNEOF) X(POLLRDHUP) XEND }; static struct xlat sigaction_flags[] = {