# HG changeset patch # User Taylor R Campbell # Date 1739190773 0 # Mon Feb 10 12:32:53 2025 +0000 # Branch trunk # Node ID bd46f7b300eba1f8fca955c84b2b02630216b1d9 # Parent c96fe1ab6a49c6b24bccc49f7ccb16b325ab0468 # EXP-Topic riastradh-pr59056-pollhup t_poll: Extend err test to cover POLLNVAL too. Prompted by: PR kern/59056: poll POLLHUP bugs diff -r c96fe1ab6a49 -r bd46f7b300eb tests/lib/libc/sys/t_poll.c --- a/tests/lib/libc/sys/t_poll.c Sun Sep 20 03:58:19 2026 +0000 +++ b/tests/lib/libc/sys/t_poll.c Mon Feb 10 12:32:53 2025 +0000 @@ -230,16 +230,28 @@ ATF_TC_HEAD(err, tc) ATF_TC_BODY(err, tc) { struct pollfd pfd; - int fd = 0; + int fd = 0, invalidfd; + int nfds; pfd.fd = fd; pfd.events = POLLIN; errno = 0; - ATF_REQUIRE_ERRNO(EFAULT, poll((struct pollfd *)-1, 1, -1) == -1); + ATF_CHECK_ERRNO(EFAULT, poll((struct pollfd *)-1, 1, -1) == -1); errno = 0; - ATF_REQUIRE_ERRNO(EINVAL, poll(&pfd, 1, -2) == -1); + ATF_CHECK_ERRNO(EINVAL, poll(&pfd, 1, -2) == -1); + + RL(invalidfd = fcntl(0, F_MAXFD)); + invalidfd++; + pfd.fd = invalidfd; + errno = 0; + RL(nfds = poll(&pfd, 1, 0)); + ATF_CHECK_EQ_MSG(nfds, 1, "nfds=%d", nfds); + ATF_CHECK_EQ_MSG(pfd.fd, invalidfd, + "pfd.fd=%d fd=%d", pfd.fd, invalidfd); + ATF_CHECK_EQ_MSG(pfd.revents, POLLNVAL, + "pfd.revents=0x%x expected=0x%x", pfd.revents, POLLNVAL); } static const char fifo_path[] = "pollhup_fifo"; # HG changeset patch # User Taylor R Campbell # Date 1739193011 0 # Mon Feb 10 13:10:11 2025 +0000 # Branch trunk # Node ID 608b37735c704a5bd5c88da4b813b5755017ebc7 # Parent bd46f7b300eba1f8fca955c84b2b02630216b1d9 # EXP-Topic riastradh-pr59056-pollhup t_poll: Fix delayed_write checks. 1. Check the results of the poll(2) call that sleeps, not a zero-timeout call instead -- not sure how that crept in. 2. Do also check a zero-timeout call at the end to make sure the event state is persistent. PR kern/59056: poll POLLHUP bugs diff -r bd46f7b300eb -r 608b37735c70 tests/lib/libc/sys/t_poll.c --- a/tests/lib/libc/sys/t_poll.c Mon Feb 10 12:32:53 2025 +0000 +++ b/tests/lib/libc/sys/t_poll.c Mon Feb 10 13:10:11 2025 +0000 @@ -595,7 +595,6 @@ check_pollclosed_delayed_write(int write * exclusive with POLLOUT). Except we _do_ return POLLHUP * instead of POLLOUT for terminals. */ - RL(nfds = poll(&pfd, 1, 0)); ATF_CHECK_EQ_MSG(nfds, 1, "nfds=%d", nfds); ATF_CHECK_EQ_MSG(pfd.fd, writefd, "pfd.fd=%d writefd=%d", pfd.fd, writefd); @@ -617,6 +616,18 @@ check_pollclosed_delayed_write(int write */ check_write_fail(writefd, writeerror); check_write_fail(writefd, writeerror); + + /* + * POLLHUP/POLLOUT state should be persistent. + */ + RL(nfds = poll(&pfd, 1, 0)); + ATF_CHECK_EQ_MSG(nfds, 1, "nfds=%d", nfds); + ATF_CHECK_EQ_MSG(pfd.fd, writefd, "pfd.fd=%d writefd=%d", + pfd.fd, writefd); + ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), expected, + "revents=0x%x expected=0x%x" + " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", + pfd.revents, expected, POLLOUT, POLLHUP, POLLIN); } static void # HG changeset patch # User Taylor R Campbell # Date 1739192558 0 # Mon Feb 10 13:02:38 2025 +0000 # Branch trunk # Node ID c47a1089027a4307f3b08214df66e9e02b3bc766 # Parent 608b37735c704a5bd5c88da4b813b5755017ebc7 # EXP-Topic riastradh-pr59056-pollhup t_poll: Rename tests pollclosed -> pollclosedpeer. These test what happens when we're polling a file descriptor for some two-ended thing (pipe, fifo, socket, pty) and the _other_ end is closed. This is to prepare for adding a suite of tests for when _this_ end is closed, which I'll reuse the name `pollclosed' for. Prompted by: PR kern/59056: poll POLLHUP bugs diff -r 608b37735c70 -r c47a1089027a tests/lib/libc/sys/t_poll.c --- a/tests/lib/libc/sys/t_poll.c Mon Feb 10 13:10:11 2025 +0000 +++ b/tests/lib/libc/sys/t_poll.c Mon Feb 10 13:02:38 2025 +0000 @@ -563,7 +563,7 @@ check_read_eof(int readfd) } static void -check_pollclosed_delayed_write(int writefd, int readfd, +check_pollclosedpeer_delayed_write(int writefd, int readfd, int expected, int writeerror) { struct pollfd pfd = { .fd = writefd, .events = POLLOUT }; @@ -631,21 +631,21 @@ check_pollclosed_delayed_write(int write } static void -check_pollclosed_delayed_write_fifopipesocket(int writefd, int readfd) +check_pollclosedpeer_delayed_write_fifopipesocket(int writefd, int readfd) { - check_pollclosed_delayed_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_delayed_write(writefd, readfd, POLLOUT, EPIPE); } static void -check_pollclosed_delayed_write_terminal(int writefd, int readfd) +check_pollclosedpeer_delayed_write_terminal(int writefd, int readfd) { - check_pollclosed_delayed_write(writefd, readfd, POLLHUP, EIO); + check_pollclosedpeer_delayed_write(writefd, readfd, POLLHUP, EIO); } static void -check_pollclosed_delayed_read(int readfd, int writefd, int pollhup) +check_pollclosedpeer_delayed_read(int readfd, int writefd, int pollhup) { struct pollfd pfd; struct timespec start, end, delta; @@ -716,21 +716,21 @@ check_pollclosed_delayed_read(int readfd } static void -check_pollclosed_delayed_read_devfifopipe(int readfd, int writefd) +check_pollclosedpeer_delayed_read_devfifopipe(int readfd, int writefd) { - check_pollclosed_delayed_read(readfd, writefd, POLLHUP); + check_pollclosedpeer_delayed_read(readfd, writefd, POLLHUP); } static void -check_pollclosed_delayed_read_socket(int readfd, int writefd) +check_pollclosedpeer_delayed_read_socket(int readfd, int writefd) { - check_pollclosed_delayed_read(readfd, writefd, /*no POLLHUP*/0); + check_pollclosedpeer_delayed_read(readfd, writefd, /*no POLLHUP*/0); } static void -check_pollclosed_delayed_process(int pollfd, int closefd, +check_pollclosedpeer_delayed_process(int pollfd, int closefd, void (*check_pollhup)(int, int)) { pid_t pid; @@ -768,7 +768,7 @@ check_pollclosed_delayed_process(int pol } static void * -check_pollclosed_thread(void *cookie) +check_pollclosedpeer_thread(void *cookie) { int *closefdp = cookie; @@ -779,7 +779,7 @@ check_pollclosed_thread(void *cookie) } static void -check_pollclosed_delayed_thread(int pollfd, int closefd, +check_pollclosedpeer_delayed_thread(int pollfd, int closefd, void (*check_pollhup)(int, int)) { pthread_t t; @@ -788,7 +788,7 @@ check_pollclosed_delayed_thread(int poll * Create a thread to close closefd (in this process, not a * child) after a 2sec delay. */ - RZ(pthread_create(&t, NULL, &check_pollclosed_thread, &closefd)); + RZ(pthread_create(&t, NULL, &check_pollclosedpeer_thread, &closefd)); /* * Test poll(2). @@ -802,7 +802,7 @@ check_pollclosed_delayed_thread(int poll } static void -check_pollclosed_immediate_write(int writefd, int readfd, int expected, +check_pollclosedpeer_immediate_write(int writefd, int readfd, int expected, int writeerror) { struct pollfd pfd = { .fd = writefd, .events = POLLOUT }; @@ -839,7 +839,7 @@ check_pollclosed_immediate_write(int wri } static void -check_pollclosed_immediate_readnone(int readfd, int writefd, int pollhup) +check_pollclosedpeer_immediate_readnone(int readfd, int writefd, int pollhup) { struct pollfd pfd = { .fd = readfd, .events = POLLIN }; int nfds; @@ -873,7 +873,7 @@ check_pollclosed_immediate_readnone(int } static void -check_pollclosed_immediate_readsome(int readfd, int writefd, int pollhup) +check_pollclosedpeer_immediate_readsome(int readfd, int writefd, int pollhup) { struct pollfd pfd; char buf[BUFSIZ]; @@ -1107,43 +1107,43 @@ pollclosed_socketpair1_setup(int *writef * cases. */ -ATF_TC(pollclosed_fifo0_immediate_writefull); -ATF_TC_HEAD(pollclosed_fifo0_immediate_writefull, tc) +ATF_TC(pollclosedpeer_fifo0_immediate_writefull); +ATF_TC_HEAD(pollclosedpeer_fifo0_immediate_writefull, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_immediate_writefull, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_immediate_writefull, tc) { int writefd, readfd; pollclosed_fifo0_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_fifo0_immediate_writeempty); -ATF_TC_HEAD(pollclosed_fifo0_immediate_writeempty, tc) +ATF_TC(pollclosedpeer_fifo0_immediate_writeempty); +ATF_TC_HEAD(pollclosedpeer_fifo0_immediate_writeempty, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_immediate_writeempty, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_immediate_writeempty, tc) { int writefd, readfd; pollclosed_fifo0_setup(&writefd, &readfd); /* don't fill the pipe buf */ - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_fifo0_immediate_readsome); -ATF_TC_HEAD(pollclosed_fifo0_immediate_readsome, tc) +ATF_TC(pollclosedpeer_fifo0_immediate_readsome); +ATF_TC_HEAD(pollclosedpeer_fifo0_immediate_readsome, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_immediate_readsome, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_immediate_readsome, tc) { int writefd, readfd; @@ -1155,47 +1155,47 @@ ATF_TC_BODY(pollclosed_fifo0_immediate_r pollclosed_fifo1_setup(&writefd, &readfd); /* reverse r/w */ fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readsome(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_fifo0_immediate_readnone); -ATF_TC_HEAD(pollclosed_fifo0_immediate_readnone, tc) +ATF_TC(pollclosedpeer_fifo0_immediate_readnone); +ATF_TC_HEAD(pollclosedpeer_fifo0_immediate_readnone, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_immediate_readnone, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_immediate_readnone, tc) { int writefd, readfd; pollclosed_fifo1_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill the pipe buf */ - check_pollclosed_immediate_readnone(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readnone(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_fifo0_delayed_process_write); -ATF_TC_HEAD(pollclosed_fifo0_delayed_process_write, tc) +ATF_TC(pollclosedpeer_fifo0_delayed_process_write); +ATF_TC_HEAD(pollclosedpeer_fifo0_delayed_process_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_delayed_process_write, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_delayed_process_write, tc) { int writefd, readfd; pollclosed_fifo0_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_process(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_process(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_fifo0_delayed_process_read); -ATF_TC_HEAD(pollclosed_fifo0_delayed_process_read, tc) +ATF_TC(pollclosedpeer_fifo0_delayed_process_read); +ATF_TC_HEAD(pollclosedpeer_fifo0_delayed_process_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_delayed_process_read, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_delayed_process_read, tc) { int writefd, readfd; @@ -1208,33 +1208,33 @@ ATF_TC_BODY(pollclosed_fifo0_delayed_pro pollclosed_fifo1_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_fifo0_delayed_thread_write); -ATF_TC_HEAD(pollclosed_fifo0_delayed_thread_write, tc) +ATF_TC(pollclosedpeer_fifo0_delayed_thread_write); +ATF_TC_HEAD(pollclosedpeer_fifo0_delayed_thread_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_delayed_thread_write, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_delayed_thread_write, tc) { int writefd, readfd; pollclosed_fifo0_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_fifo0_delayed_thread_read); -ATF_TC_HEAD(pollclosed_fifo0_delayed_thread_read, tc) +ATF_TC(pollclosedpeer_fifo0_delayed_thread_read); +ATF_TC_HEAD(pollclosedpeer_fifo0_delayed_thread_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo0_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedpeer_fifo0_delayed_thread_read, tc) { int writefd, readfd; @@ -1247,47 +1247,47 @@ ATF_TC_BODY(pollclosed_fifo0_delayed_thr pollclosed_fifo1_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_thread(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_thread(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_fifo1_immediate_writefull); -ATF_TC_HEAD(pollclosed_fifo1_immediate_writefull, tc) +ATF_TC(pollclosedpeer_fifo1_immediate_writefull); +ATF_TC_HEAD(pollclosedpeer_fifo1_immediate_writefull, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_immediate_writefull, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_immediate_writefull, tc) { int writefd, readfd; pollclosed_fifo1_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_fifo1_immediate_writeempty); -ATF_TC_HEAD(pollclosed_fifo1_immediate_writeempty, tc) +ATF_TC(pollclosedpeer_fifo1_immediate_writeempty); +ATF_TC_HEAD(pollclosedpeer_fifo1_immediate_writeempty, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_immediate_writeempty, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_immediate_writeempty, tc) { int writefd, readfd; pollclosed_fifo1_setup(&writefd, &readfd); /* don't fill the pipe buf */ - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_fifo1_immediate_readsome); -ATF_TC_HEAD(pollclosed_fifo1_immediate_readsome, tc) +ATF_TC(pollclosedpeer_fifo1_immediate_readsome); +ATF_TC_HEAD(pollclosedpeer_fifo1_immediate_readsome, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_immediate_readsome, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_immediate_readsome, tc) { int writefd, readfd; @@ -1299,47 +1299,47 @@ ATF_TC_BODY(pollclosed_fifo1_immediate_r pollclosed_fifo0_setup(&writefd, &readfd); /* reverse r/w */ fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readsome(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_fifo1_immediate_readnone); -ATF_TC_HEAD(pollclosed_fifo1_immediate_readnone, tc) +ATF_TC(pollclosedpeer_fifo1_immediate_readnone); +ATF_TC_HEAD(pollclosedpeer_fifo1_immediate_readnone, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_immediate_readnone, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_immediate_readnone, tc) { int writefd, readfd; pollclosed_fifo0_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill the pipe buf */ - check_pollclosed_immediate_readnone(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readnone(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_fifo1_delayed_process_write); -ATF_TC_HEAD(pollclosed_fifo1_delayed_process_write, tc) +ATF_TC(pollclosedpeer_fifo1_delayed_process_write); +ATF_TC_HEAD(pollclosedpeer_fifo1_delayed_process_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_delayed_process_write, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_delayed_process_write, tc) { int writefd, readfd; pollclosed_fifo1_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_process(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_process(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_fifo1_delayed_process_read); -ATF_TC_HEAD(pollclosed_fifo1_delayed_process_read, tc) +ATF_TC(pollclosedpeer_fifo1_delayed_process_read); +ATF_TC_HEAD(pollclosedpeer_fifo1_delayed_process_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_delayed_process_read, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_delayed_process_read, tc) { int writefd, readfd; @@ -1352,33 +1352,33 @@ ATF_TC_BODY(pollclosed_fifo1_delayed_pro pollclosed_fifo0_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_fifo1_delayed_thread_write); -ATF_TC_HEAD(pollclosed_fifo1_delayed_thread_write, tc) +ATF_TC(pollclosedpeer_fifo1_delayed_thread_write); +ATF_TC_HEAD(pollclosedpeer_fifo1_delayed_thread_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_delayed_thread_write, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_delayed_thread_write, tc) { int writefd, readfd; pollclosed_fifo1_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_fifo1_delayed_thread_read); -ATF_TC_HEAD(pollclosed_fifo1_delayed_thread_read, tc) +ATF_TC(pollclosedpeer_fifo1_delayed_thread_read); +ATF_TC_HEAD(pollclosedpeer_fifo1_delayed_thread_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second opener of a named pipe"); } -ATF_TC_BODY(pollclosed_fifo1_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedpeer_fifo1_delayed_thread_read, tc) { int writefd, readfd; @@ -1391,17 +1391,17 @@ ATF_TC_BODY(pollclosed_fifo1_delayed_thr pollclosed_fifo0_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_pipe_immediate_writefull); -ATF_TC_HEAD(pollclosed_pipe_immediate_writefull, tc) +ATF_TC(pollclosedpeer_pipe_immediate_writefull); +ATF_TC_HEAD(pollclosedpeer_pipe_immediate_writefull, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_immediate_writefull, tc) +ATF_TC_BODY(pollclosedpeer_pipe_immediate_writefull, tc) { int writefd, readfd; @@ -1415,16 +1415,16 @@ ATF_TC_BODY(pollclosed_pipe_immediate_wr pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_pipe_immediate_writeempty); -ATF_TC_HEAD(pollclosed_pipe_immediate_writeempty, tc) +ATF_TC(pollclosedpeer_pipe_immediate_writeempty); +ATF_TC_HEAD(pollclosedpeer_pipe_immediate_writeempty, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_immediate_writeempty, tc) +ATF_TC_BODY(pollclosedpeer_pipe_immediate_writeempty, tc) { int writefd, readfd; @@ -1438,46 +1438,46 @@ ATF_TC_BODY(pollclosed_pipe_immediate_wr pollclosed_pipe_setup(&writefd, &readfd); /* don't fill pipe buf */ - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_pipe_immediate_readsome); -ATF_TC_HEAD(pollclosed_pipe_immediate_readsome, tc) +ATF_TC(pollclosedpeer_pipe_immediate_readsome); +ATF_TC_HEAD(pollclosedpeer_pipe_immediate_readsome, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_immediate_readsome, tc) +ATF_TC_BODY(pollclosedpeer_pipe_immediate_readsome, tc) { int writefd, readfd; pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readsome(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_pipe_immediate_readnone); -ATF_TC_HEAD(pollclosed_pipe_immediate_readnone, tc) +ATF_TC(pollclosedpeer_pipe_immediate_readnone); +ATF_TC_HEAD(pollclosedpeer_pipe_immediate_readnone, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_immediate_readnone, tc) +ATF_TC_BODY(pollclosedpeer_pipe_immediate_readnone, tc) { int writefd, readfd; pollclosed_pipe_setup(&writefd, &readfd); /* don't fill pipe buf */ - check_pollclosed_immediate_readnone(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readnone(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_pipe_delayed_process_write); -ATF_TC_HEAD(pollclosed_pipe_delayed_process_write, tc) +ATF_TC(pollclosedpeer_pipe_delayed_process_write); +ATF_TC_HEAD(pollclosedpeer_pipe_delayed_process_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_delayed_process_write, tc) +ATF_TC_BODY(pollclosedpeer_pipe_delayed_process_write, tc) { int writefd, readfd; @@ -1491,33 +1491,33 @@ ATF_TC_BODY(pollclosed_pipe_delayed_proc pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_process(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_process(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_pipe_delayed_process_read); -ATF_TC_HEAD(pollclosed_pipe_delayed_process_read, tc) +ATF_TC(pollclosedpeer_pipe_delayed_process_read); +ATF_TC_HEAD(pollclosedpeer_pipe_delayed_process_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_delayed_process_read, tc) +ATF_TC_BODY(pollclosedpeer_pipe_delayed_process_read, tc) { int writefd, readfd; pollclosed_pipe_setup(&writefd, &readfd); /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_pipe_delayed_thread_write); -ATF_TC_HEAD(pollclosed_pipe_delayed_thread_write, tc) +ATF_TC(pollclosedpeer_pipe_delayed_thread_write); +ATF_TC_HEAD(pollclosedpeer_pipe_delayed_thread_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_delayed_thread_write, tc) +ATF_TC_BODY(pollclosedpeer_pipe_delayed_thread_write, tc) { int writefd, readfd; @@ -1531,63 +1531,63 @@ ATF_TC_BODY(pollclosed_pipe_delayed_thre pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_pipe_delayed_thread_read); -ATF_TC_HEAD(pollclosed_pipe_delayed_thread_read, tc) +ATF_TC(pollclosedpeer_pipe_delayed_thread_read); +ATF_TC_HEAD(pollclosedpeer_pipe_delayed_thread_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with a closed pipe"); } -ATF_TC_BODY(pollclosed_pipe_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedpeer_pipe_delayed_thread_read, tc) { int writefd, readfd; pollclosed_pipe_setup(&writefd, &readfd); /* don't fill pipe buf */ - check_pollclosed_delayed_thread(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_thread(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_ptyapp_immediate_writefull); -ATF_TC_HEAD(pollclosed_ptyapp_immediate_writefull, tc) +ATF_TC(pollclosedpeer_ptyapp_immediate_writefull); +ATF_TC_HEAD(pollclosedpeer_ptyapp_immediate_writefull, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_immediate_writefull, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_immediate_writefull, tc) { int writefd, readfd; pollclosed_ptyapp_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_write(writefd, readfd, POLLHUP, EIO); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLHUP, EIO); } -ATF_TC(pollclosed_ptyapp_immediate_writeempty); -ATF_TC_HEAD(pollclosed_ptyapp_immediate_writeempty, tc) +ATF_TC(pollclosedpeer_ptyapp_immediate_writeempty); +ATF_TC_HEAD(pollclosedpeer_ptyapp_immediate_writeempty, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_immediate_writeempty, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_immediate_writeempty, tc) { int writefd, readfd; pollclosed_ptyapp_setup(&writefd, &readfd); /* don't fill the pipe buf */ - check_pollclosed_immediate_write(writefd, readfd, POLLHUP, EIO); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLHUP, EIO); } -ATF_TC(pollclosed_ptyapp_immediate_readsome); -ATF_TC_HEAD(pollclosed_ptyapp_immediate_readsome, tc) +ATF_TC(pollclosedpeer_ptyapp_immediate_readsome); +ATF_TC_HEAD(pollclosedpeer_ptyapp_immediate_readsome, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_immediate_readsome, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_immediate_readsome, tc) { int writefd, readfd; @@ -1599,16 +1599,16 @@ ATF_TC_BODY(pollclosed_ptyapp_immediate_ pollclosed_ptyhost_setup(&writefd, &readfd); /* reverse r/w */ fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readsome(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_ptyapp_immediate_readnone); -ATF_TC_HEAD(pollclosed_ptyapp_immediate_readnone, tc) +ATF_TC(pollclosedpeer_ptyapp_immediate_readnone); +ATF_TC_HEAD(pollclosedpeer_ptyapp_immediate_readnone, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_immediate_readnone, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_immediate_readnone, tc) { int writefd, readfd; @@ -1620,16 +1620,16 @@ ATF_TC_BODY(pollclosed_ptyapp_immediate_ pollclosed_ptyhost_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill the pipe buf */ - check_pollclosed_immediate_readnone(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readnone(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_ptyapp_delayed_process_write); -ATF_TC_HEAD(pollclosed_ptyapp_delayed_process_write, tc) +ATF_TC(pollclosedpeer_ptyapp_delayed_process_write); +ATF_TC_HEAD(pollclosedpeer_ptyapp_delayed_process_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_delayed_process_write, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_delayed_process_write, tc) { int writefd, readfd; @@ -1641,17 +1641,17 @@ ATF_TC_BODY(pollclosed_ptyapp_delayed_pr pollclosed_ptyapp_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_process(writefd, readfd, - &check_pollclosed_delayed_write_terminal); + check_pollclosedpeer_delayed_process(writefd, readfd, + &check_pollclosedpeer_delayed_write_terminal); } -ATF_TC(pollclosed_ptyapp_delayed_process_read); -ATF_TC_HEAD(pollclosed_ptyapp_delayed_process_read, tc) +ATF_TC(pollclosedpeer_ptyapp_delayed_process_read); +ATF_TC_HEAD(pollclosedpeer_ptyapp_delayed_process_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_delayed_process_read, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_delayed_process_read, tc) { int writefd, readfd; @@ -1663,17 +1663,17 @@ ATF_TC_BODY(pollclosed_ptyapp_delayed_pr pollclosed_ptyhost_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_ptyapp_delayed_thread_write); -ATF_TC_HEAD(pollclosed_ptyapp_delayed_thread_write, tc) +ATF_TC(pollclosedpeer_ptyapp_delayed_thread_write); +ATF_TC_HEAD(pollclosedpeer_ptyapp_delayed_thread_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_delayed_thread_write, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_delayed_thread_write, tc) { int writefd, readfd; @@ -1685,17 +1685,17 @@ ATF_TC_BODY(pollclosed_ptyapp_delayed_th pollclosed_ptyapp_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_terminal); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_terminal); } -ATF_TC(pollclosed_ptyapp_delayed_thread_read); -ATF_TC_HEAD(pollclosed_ptyapp_delayed_thread_read, tc) +ATF_TC(pollclosedpeer_ptyapp_delayed_thread_read); +ATF_TC_HEAD(pollclosedpeer_ptyapp_delayed_thread_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty application side"); } -ATF_TC_BODY(pollclosed_ptyapp_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedpeer_ptyapp_delayed_thread_read, tc) { int writefd, readfd; @@ -1707,47 +1707,47 @@ ATF_TC_BODY(pollclosed_ptyapp_delayed_th pollclosed_ptyhost_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_ptyhost_immediate_writefull); -ATF_TC_HEAD(pollclosed_ptyhost_immediate_writefull, tc) +ATF_TC(pollclosedpeer_ptyhost_immediate_writefull); +ATF_TC_HEAD(pollclosedpeer_ptyhost_immediate_writefull, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_immediate_writefull, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_immediate_writefull, tc) { int writefd, readfd; pollclosed_ptyhost_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_write(writefd, readfd, POLLHUP, EIO); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLHUP, EIO); } -ATF_TC(pollclosed_ptyhost_immediate_writeempty); -ATF_TC_HEAD(pollclosed_ptyhost_immediate_writeempty, tc) +ATF_TC(pollclosedpeer_ptyhost_immediate_writeempty); +ATF_TC_HEAD(pollclosedpeer_ptyhost_immediate_writeempty, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_immediate_writeempty, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_immediate_writeempty, tc) { int writefd, readfd; pollclosed_ptyhost_setup(&writefd, &readfd); /* don't fill the pipe buf */ - check_pollclosed_immediate_write(writefd, readfd, POLLHUP, EIO); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLHUP, EIO); } -ATF_TC(pollclosed_ptyhost_immediate_readsome); -ATF_TC_HEAD(pollclosed_ptyhost_immediate_readsome, tc) +ATF_TC(pollclosedpeer_ptyhost_immediate_readsome); +ATF_TC_HEAD(pollclosedpeer_ptyhost_immediate_readsome, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_immediate_readsome, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_immediate_readsome, tc) { int writefd, readfd; @@ -1759,16 +1759,16 @@ ATF_TC_BODY(pollclosed_ptyhost_immediate pollclosed_ptyapp_setup(&writefd, &readfd); /* reverse r/w */ fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readsome(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_ptyhost_immediate_readnone); -ATF_TC_HEAD(pollclosed_ptyhost_immediate_readnone, tc) +ATF_TC(pollclosedpeer_ptyhost_immediate_readnone); +ATF_TC_HEAD(pollclosedpeer_ptyhost_immediate_readnone, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_immediate_readnone, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_immediate_readnone, tc) { int writefd, readfd; @@ -1780,32 +1780,32 @@ ATF_TC_BODY(pollclosed_ptyhost_immediate pollclosed_ptyapp_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill the pipe buf */ - check_pollclosed_immediate_readnone(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_readnone(readfd, writefd, POLLHUP); } -ATF_TC(pollclosed_ptyhost_delayed_process_write); -ATF_TC_HEAD(pollclosed_ptyhost_delayed_process_write, tc) +ATF_TC(pollclosedpeer_ptyhost_delayed_process_write); +ATF_TC_HEAD(pollclosedpeer_ptyhost_delayed_process_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_delayed_process_write, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_delayed_process_write, tc) { int writefd, readfd; pollclosed_ptyhost_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_process(writefd, readfd, - &check_pollclosed_delayed_write_terminal); + check_pollclosedpeer_delayed_process(writefd, readfd, + &check_pollclosedpeer_delayed_write_terminal); } -ATF_TC(pollclosed_ptyhost_delayed_process_read); -ATF_TC_HEAD(pollclosed_ptyhost_delayed_process_read, tc) +ATF_TC(pollclosedpeer_ptyhost_delayed_process_read); +ATF_TC_HEAD(pollclosedpeer_ptyhost_delayed_process_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_delayed_process_read, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_delayed_process_read, tc) { int writefd, readfd; @@ -1817,33 +1817,33 @@ ATF_TC_BODY(pollclosed_ptyhost_delayed_p pollclosed_ptyapp_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_ptyhost_delayed_thread_write); -ATF_TC_HEAD(pollclosed_ptyhost_delayed_thread_write, tc) +ATF_TC(pollclosedpeer_ptyhost_delayed_thread_write); +ATF_TC_HEAD(pollclosedpeer_ptyhost_delayed_thread_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_delayed_thread_write, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_delayed_thread_write, tc) { int writefd, readfd; pollclosed_ptyhost_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_terminal); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_terminal); } -ATF_TC(pollclosed_ptyhost_delayed_thread_read); -ATF_TC_HEAD(pollclosed_ptyhost_delayed_thread_read, tc) +ATF_TC(pollclosedpeer_ptyhost_delayed_thread_read); +ATF_TC_HEAD(pollclosedpeer_ptyhost_delayed_thread_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the pty host side"); } -ATF_TC_BODY(pollclosed_ptyhost_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedpeer_ptyhost_delayed_thread_read, tc) { int writefd, readfd; @@ -1855,256 +1855,260 @@ ATF_TC_BODY(pollclosed_ptyhost_delayed_t pollclosed_ptyapp_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_thread(readfd, writefd, - &check_pollclosed_delayed_read_devfifopipe); + check_pollclosedpeer_delayed_thread(readfd, writefd, + &check_pollclosedpeer_delayed_read_devfifopipe); } -ATF_TC(pollclosed_socketpair0_immediate_writefull); -ATF_TC_HEAD(pollclosed_socketpair0_immediate_writefull, tc) +ATF_TC(pollclosedpeer_socketpair0_immediate_writefull); +ATF_TC_HEAD(pollclosedpeer_socketpair0_immediate_writefull, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_immediate_writefull, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_immediate_writefull, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_socketpair0_immediate_writeempty); -ATF_TC_HEAD(pollclosed_socketpair0_immediate_writeempty, tc) +ATF_TC(pollclosedpeer_socketpair0_immediate_writeempty); +ATF_TC_HEAD(pollclosedpeer_socketpair0_immediate_writeempty, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_immediate_writeempty, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_immediate_writeempty, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); /* don't fill the pipe buf */ - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_socketpair0_immediate_readsome); -ATF_TC_HEAD(pollclosed_socketpair0_immediate_readsome, tc) +ATF_TC(pollclosedpeer_socketpair0_immediate_readsome); +ATF_TC_HEAD(pollclosedpeer_socketpair0_immediate_readsome, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_immediate_readsome, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_immediate_readsome, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); /* reverse r/w */ fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, /*no POLLHUP*/0); + check_pollclosedpeer_immediate_readsome(readfd, writefd, + /*no POLLHUP*/0); } -ATF_TC(pollclosed_socketpair0_immediate_readnone); -ATF_TC_HEAD(pollclosed_socketpair0_immediate_readnone, tc) +ATF_TC(pollclosedpeer_socketpair0_immediate_readnone); +ATF_TC_HEAD(pollclosedpeer_socketpair0_immediate_readnone, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_immediate_readnone, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_immediate_readnone, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill the pipe buf */ - check_pollclosed_immediate_readnone(readfd, writefd, /*no POLLHUP*/0); + check_pollclosedpeer_immediate_readnone(readfd, writefd, + /*no POLLHUP*/0); } -ATF_TC(pollclosed_socketpair0_delayed_process_write); -ATF_TC_HEAD(pollclosed_socketpair0_delayed_process_write, tc) +ATF_TC(pollclosedpeer_socketpair0_delayed_process_write); +ATF_TC_HEAD(pollclosedpeer_socketpair0_delayed_process_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_delayed_process_write, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_delayed_process_write, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_process(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_process(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_socketpair0_delayed_process_read); -ATF_TC_HEAD(pollclosed_socketpair0_delayed_process_read, tc) +ATF_TC(pollclosedpeer_socketpair0_delayed_process_read); +ATF_TC_HEAD(pollclosedpeer_socketpair0_delayed_process_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_delayed_process_read, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_delayed_process_read, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_socket); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_socket); } -ATF_TC(pollclosed_socketpair0_delayed_thread_write); -ATF_TC_HEAD(pollclosed_socketpair0_delayed_thread_write, tc) +ATF_TC(pollclosedpeer_socketpair0_delayed_thread_write); +ATF_TC_HEAD(pollclosedpeer_socketpair0_delayed_thread_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_delayed_thread_write, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_delayed_thread_write, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_socketpair0_delayed_thread_read); -ATF_TC_HEAD(pollclosed_socketpair0_delayed_thread_read, tc) +ATF_TC(pollclosedpeer_socketpair0_delayed_thread_read); +ATF_TC_HEAD(pollclosedpeer_socketpair0_delayed_thread_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the first half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair0_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedpeer_socketpair0_delayed_thread_read, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_thread(readfd, writefd, - &check_pollclosed_delayed_read_socket); + check_pollclosedpeer_delayed_thread(readfd, writefd, + &check_pollclosedpeer_delayed_read_socket); } -ATF_TC(pollclosed_socketpair1_immediate_writefull); -ATF_TC_HEAD(pollclosed_socketpair1_immediate_writefull, tc) +ATF_TC(pollclosedpeer_socketpair1_immediate_writefull); +ATF_TC_HEAD(pollclosedpeer_socketpair1_immediate_writefull, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_immediate_writefull, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_immediate_writefull, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_socketpair1_immediate_writeempty); -ATF_TC_HEAD(pollclosed_socketpair1_immediate_writeempty, tc) +ATF_TC(pollclosedpeer_socketpair1_immediate_writeempty); +ATF_TC_HEAD(pollclosedpeer_socketpair1_immediate_writeempty, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_immediate_writeempty, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_immediate_writeempty, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); /* don't fill the pipe buf */ - check_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_socketpair1_immediate_readsome); -ATF_TC_HEAD(pollclosed_socketpair1_immediate_readsome, tc) +ATF_TC(pollclosedpeer_socketpair1_immediate_readsome); +ATF_TC_HEAD(pollclosedpeer_socketpair1_immediate_readsome, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_immediate_readsome, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_immediate_readsome, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); /* reverse r/w */ fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, /*no POLLHUP*/0); + check_pollclosedpeer_immediate_readsome(readfd, writefd, + /*no POLLHUP*/0); } -ATF_TC(pollclosed_socketpair1_immediate_readnone); -ATF_TC_HEAD(pollclosed_socketpair1_immediate_readnone, tc) +ATF_TC(pollclosedpeer_socketpair1_immediate_readnone); +ATF_TC_HEAD(pollclosedpeer_socketpair1_immediate_readnone, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_immediate_readnone, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_immediate_readnone, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill the pipe buf */ - check_pollclosed_immediate_readnone(readfd, writefd, /*no POLLHUP*/0); + check_pollclosedpeer_immediate_readnone(readfd, writefd, + /*no POLLHUP*/0); } -ATF_TC(pollclosed_socketpair1_delayed_process_write); -ATF_TC_HEAD(pollclosed_socketpair1_delayed_process_write, tc) +ATF_TC(pollclosedpeer_socketpair1_delayed_process_write); +ATF_TC_HEAD(pollclosedpeer_socketpair1_delayed_process_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_delayed_process_write, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_delayed_process_write, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_process(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_process(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_socketpair1_delayed_process_read); -ATF_TC_HEAD(pollclosed_socketpair1_delayed_process_read, tc) +ATF_TC(pollclosedpeer_socketpair1_delayed_process_read); +ATF_TC_HEAD(pollclosedpeer_socketpair1_delayed_process_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_delayed_process_read, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_delayed_process_read, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_socket); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_socket); } -ATF_TC(pollclosed_socketpair1_delayed_thread_write); -ATF_TC_HEAD(pollclosed_socketpair1_delayed_thread_write, tc) +ATF_TC(pollclosedpeer_socketpair1_delayed_thread_write); +ATF_TC_HEAD(pollclosedpeer_socketpair1_delayed_thread_write, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_delayed_thread_write, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_delayed_thread_write, tc) { int writefd, readfd; pollclosed_socketpair1_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); } -ATF_TC(pollclosed_socketpair1_delayed_thread_read); -ATF_TC_HEAD(pollclosed_socketpair1_delayed_thread_read, tc) +ATF_TC(pollclosedpeer_socketpair1_delayed_thread_read); +ATF_TC_HEAD(pollclosedpeer_socketpair1_delayed_thread_read, tc) { atf_tc_set_md_var(tc, "descr", "Checks POLLHUP with closing the second half of a socketpair"); } -ATF_TC_BODY(pollclosed_socketpair1_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedpeer_socketpair1_delayed_thread_read, tc) { int writefd, readfd; pollclosed_socketpair0_setup(&writefd, &readfd); /* reverse r/w */ /* don't fill pipe buf */ - check_pollclosed_delayed_process(readfd, writefd, - &check_pollclosed_delayed_read_socket); + check_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_socket); } ATF_TP_ADD_TCS(tp) @@ -2118,69 +2122,69 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, fifo_hup1); ATF_TP_ADD_TC(tp, fifo_hup2); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_immediate_writefull); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_immediate_writefull); - ATF_TP_ADD_TC(tp, pollclosed_pipe_immediate_writefull); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_immediate_writefull); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_immediate_writefull); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_immediate_writefull); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_immediate_writefull); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_immediate_writefull); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_immediate_writefull); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_immediate_writefull); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_immediate_writefull); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_immediate_writefull); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_immediate_writefull); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_immediate_writefull); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_immediate_writeempty); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_immediate_writeempty); - ATF_TP_ADD_TC(tp, pollclosed_pipe_immediate_writeempty); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_immediate_writeempty); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_immediate_writeempty); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_immediate_writeempty); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_immediate_writeempty); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_immediate_writeempty); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_immediate_writeempty); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_immediate_writeempty); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_immediate_writeempty); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_immediate_writeempty); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_immediate_writeempty); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_immediate_writeempty); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_immediate_readsome); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_immediate_readsome); - ATF_TP_ADD_TC(tp, pollclosed_pipe_immediate_readsome); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_immediate_readsome); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_immediate_readsome); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_immediate_readsome); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_immediate_readsome); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_immediate_readsome); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_immediate_readsome); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_immediate_readsome); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_immediate_readsome); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_immediate_readsome); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_immediate_readsome); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_immediate_readsome); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_immediate_readnone); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_immediate_readnone); - ATF_TP_ADD_TC(tp, pollclosed_pipe_immediate_readnone); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_immediate_readnone); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_immediate_readnone); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_immediate_readnone); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_immediate_readnone); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_immediate_readnone); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_immediate_readnone); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_immediate_readnone); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_immediate_readnone); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_immediate_readnone); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_immediate_readnone); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_immediate_readnone); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_delayed_process_write); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_delayed_process_write); - ATF_TP_ADD_TC(tp, pollclosed_pipe_delayed_process_write); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_delayed_process_write); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_delayed_process_write); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_delayed_process_write); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_delayed_process_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_delayed_process_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_delayed_process_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_delayed_process_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_delayed_process_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_delayed_process_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_delayed_process_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_delayed_process_write); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_delayed_process_read); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_delayed_process_read); - ATF_TP_ADD_TC(tp, pollclosed_pipe_delayed_process_read); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_delayed_process_read); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_delayed_process_read); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_delayed_process_read); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_delayed_process_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_delayed_process_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_delayed_process_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_delayed_process_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_delayed_process_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_delayed_process_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_delayed_process_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_delayed_process_read); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_delayed_thread_write); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_delayed_thread_write); - ATF_TP_ADD_TC(tp, pollclosed_pipe_delayed_thread_write); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_delayed_thread_write); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_delayed_thread_write); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_delayed_thread_write); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_delayed_thread_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_delayed_thread_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_delayed_thread_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_delayed_thread_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_delayed_thread_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_delayed_thread_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_delayed_thread_write); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_delayed_thread_write); - ATF_TP_ADD_TC(tp, pollclosed_fifo0_delayed_thread_read); - ATF_TP_ADD_TC(tp, pollclosed_fifo1_delayed_thread_read); - ATF_TP_ADD_TC(tp, pollclosed_pipe_delayed_thread_read); - ATF_TP_ADD_TC(tp, pollclosed_ptyapp_delayed_thread_read); - ATF_TP_ADD_TC(tp, pollclosed_ptyhost_delayed_thread_read); - ATF_TP_ADD_TC(tp, pollclosed_socketpair0_delayed_thread_read); - ATF_TP_ADD_TC(tp, pollclosed_socketpair1_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo0_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_fifo1_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_pipe_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyapp_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_ptyhost_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_delayed_thread_read); return atf_no_error(); } # HG changeset patch # User Taylor R Campbell # Date 1790107274 0 # Tue Sep 22 20:01:14 2026 +0000 # Branch trunk # Node ID 95a742e755b0db88f8af792e8a6adaff484f62b6 # Parent c47a1089027a4307f3b08214df66e9e02b3bc766 # EXP-Topic riastradh-pr59056-pollhup t_poll: Spruce up output of poll event bits. Would be nice if we had a POLLFMT for use with snprintb(3)! PR kern/59056: poll POLLHUP bugs diff -r c47a1089027a -r 95a742e755b0 tests/lib/libc/sys/t_poll.c --- a/tests/lib/libc/sys/t_poll.c Mon Feb 10 13:02:38 2025 +0000 +++ b/tests/lib/libc/sys/t_poll.c Tue Sep 22 20:01:14 2026 +0000 @@ -49,6 +49,53 @@ #include "h_macros.h" +static void +formatbit(char **bufp, size_t *lenp, bool *firstp, const char *name) +{ + int n; + size_t k; + + n = snprintf(*bufp, *lenp, "%s%s", *firstp ? "" : ",", name); + *firstp = false; + k = ((unsigned)n >= *lenp ? *lenp : (unsigned)n); + *bufp += k; + *lenp -= k; +} + +static char * +formatpollevents(char *buf, size_t len, int events) +{ + char *start = buf; + bool first = true; + int n; + + n = snprintf(buf, len, "0x%x<", events); + if ((unsigned)n >= len) + goto out; + buf += (unsigned)n; + len -= (unsigned)n; + if (events & POLLIN) + formatbit(&buf, &len, &first, "POLLIN"); + if (events & POLLOUT) + formatbit(&buf, &len, &first, "POLLOUT"); + if (events & POLLHUP) + formatbit(&buf, &len, &first, "POLLHUP"); + if (events & POLLERR) + formatbit(&buf, &len, &first, "POLLERR"); + if (events & POLLRDNORM) + formatbit(&buf, &len, &first, "POLLRDNORM"); + if (events & POLLWRNORM) + formatbit(&buf, &len, &first, "POLLWRNORM"); + if (events & POLLRDBAND) + formatbit(&buf, &len, &first, "POLLRDBAND"); + if (events & POLLWRBAND) + formatbit(&buf, &len, &first, "POLLWRBAND"); + if (events & POLLNVAL) + formatbit(&buf, &len, &first, "POLLNVAL"); + snprintf(buf, len, ">"); +out: return start; +} + static int desc; static void @@ -166,6 +213,7 @@ ATF_TC_BODY(basic, tc) struct pollfd pfds[2]; int ret; ssize_t nwrit; + char eventbuf[128]; RL(pipe(fds)); @@ -182,26 +230,30 @@ ATF_TC_BODY(basic, tc) pfds[1].revents = -1; RL(ret = poll(&pfds[0], 1, 1)); ATF_REQUIRE_EQ_MSG(ret, 0, "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %d", pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, -1, "got: %d", pfds[1].revents); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[0].revents)); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, -1, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[1].revents)); /* Check that the write end of the pipe as reported as ready. */ pfds[0].revents = -1; pfds[1].revents = -1; RL(ret = poll(&pfds[1], 1, 1)); ATF_REQUIRE_EQ_MSG(ret, 1, "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, -1, "got: %d", pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d",\ - pfds[1].revents); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, -1, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[0].revents)); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[1].revents)); /* Check that only the write end of the pipe as reported as ready. */ pfds[0].revents = -1; pfds[1].revents = -1; RL(ret = poll(pfds, 2, 1)); ATF_REQUIRE_EQ_MSG(ret, 1, "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %d", pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d", - pfds[1].revents); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, 0, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[0].revents)); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[1].revents)); /* Write data to our pipe. */ RL(nwrit = write(fds[1], "", 1)); @@ -212,10 +264,10 @@ ATF_TC_BODY(basic, tc) pfds[1].revents = -1; RL(ret = poll(pfds, 2, 1)); ATF_REQUIRE_EQ_MSG(ret, 2, "got: %d", ret); - ATF_REQUIRE_EQ_MSG(pfds[0].revents, POLLIN, "got: %d", - pfds[0].revents); - ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %d", - pfds[1].revents); + ATF_REQUIRE_EQ_MSG(pfds[0].revents, POLLIN, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[0].revents)); + ATF_REQUIRE_EQ_MSG(pfds[1].revents, POLLOUT, "got: %s", + formatpollevents(eventbuf, sizeof(eventbuf), pfds[1].revents)); RL(close(fds[0])); RL(close(fds[1])); @@ -232,6 +284,7 @@ ATF_TC_BODY(err, tc) struct pollfd pfd; int fd = 0, invalidfd; int nfds; + char actbuf[128], expbuf[128]; pfd.fd = fd; pfd.events = POLLIN; @@ -251,7 +304,9 @@ ATF_TC_BODY(err, tc) ATF_CHECK_EQ_MSG(pfd.fd, invalidfd, "pfd.fd=%d fd=%d", pfd.fd, invalidfd); ATF_CHECK_EQ_MSG(pfd.revents, POLLNVAL, - "pfd.revents=0x%x expected=0x%x", pfd.revents, POLLNVAL); + "pfd.revents=%s expected=%s", + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), POLLNVAL)); } static const char fifo_path[] = "pollhup_fifo"; @@ -288,6 +343,7 @@ ATF_TC_BODY(fifo_inout, tc) long pipe_buf; int ret; ssize_t nwrit, nread; + char eventbuf[128]; fifo_support(); @@ -311,9 +367,11 @@ ATF_TC_BODY(fifo_inout, tc) RL(ret = poll(pfd, 2, 0)); ATF_REQUIRE_EQ_MSG(ret, 1, "got: %d", ret); ATF_REQUIRE_EQ_MSG(pfd[0].revents, 0, - "pfd[0].revents=0x%x", pfd[0].revents); + "pfd[0].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[0].revents)); ATF_REQUIRE_EQ_MSG(pfd[1].revents, POLLOUT|POLLWRNORM, - "pfd[1].revents=0x%x", pfd[1].revents); + "pfd[1].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[1].revents)); /* Write a single byte of data into the FIFO. */ RL(nwrit = write(wfd, buf, 1)); @@ -323,9 +381,11 @@ ATF_TC_BODY(fifo_inout, tc) RL(ret = poll(pfd, 2, 0)); ATF_REQUIRE_EQ_MSG(ret, 2, "got: %d", ret); ATF_REQUIRE_EQ_MSG(pfd[0].revents, POLLIN|POLLRDNORM, - "pfd[0].revents=0x%x", pfd[0].revents); + "pfd[0].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[0].revents)); ATF_REQUIRE_EQ_MSG(pfd[1].revents, POLLOUT|POLLWRNORM, - "pfd[1].revents=0x%x", pfd[1].revents); + "pfd[1].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[1].revents)); /* Read that single byte back out. */ RL(nread = read(rfd, buf, 1)); @@ -345,9 +405,11 @@ ATF_TC_BODY(fifo_inout, tc) RL(ret = poll(pfd, 2, 0)); ATF_REQUIRE_EQ_MSG(ret, 1, "got: %d", ret); ATF_REQUIRE_EQ_MSG(pfd[0].revents, POLLIN|POLLRDNORM, - "pfd[0].revents=0x%x", pfd[0].revents); + "pfd[0].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[0].revents)); ATF_REQUIRE_EQ_MSG(pfd[1].revents, 0, - "pfd[1].revents=0x%x", pfd[1].revents); + "pfd[1].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[1].revents)); /* Read a single byte of data from the FIFO. */ RL(nread = read(rfd, buf, 1)); @@ -361,9 +423,11 @@ ATF_TC_BODY(fifo_inout, tc) RL(ret = poll(pfd, 2, 0)); ATF_REQUIRE_EQ_MSG(ret, 1, "got: %d", ret); ATF_REQUIRE_EQ_MSG(pfd[0].revents, POLLIN|POLLRDNORM, - "pfd[0].revents=0x%x", pfd[0].revents); + "pfd[0].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[0].revents)); ATF_REQUIRE_EQ_MSG(pfd[1].revents, 0, - "pfd[1].revents=0x%x", pfd[1].revents); + "pfd[1].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[1].revents)); /* * Now read enough so that exactly pipe_buf space should @@ -376,7 +440,8 @@ ATF_TC_BODY(fifo_inout, tc) RL(ret = poll(pfd, 2, 0)); ATF_REQUIRE_MSG(ret >= 1, "got: %d", ret); ATF_REQUIRE_EQ_MSG(pfd[1].revents, POLLOUT|POLLWRNORM, - "pfd[1].revents=0x%x", pfd[1].revents); + "pfd[1].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[1].revents)); /* * Now read all of the data out of the FIFO and ensure that @@ -390,9 +455,11 @@ ATF_TC_BODY(fifo_inout, tc) RL(ret = poll(pfd, 2, 0)); ATF_REQUIRE_EQ_MSG(ret, 1, "got: %d", ret); ATF_REQUIRE_EQ_MSG(pfd[0].revents, 0, - "pfd[0].revents=0x%x", pfd[0].revents); + "pfd[0].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[0].revents)); ATF_REQUIRE_EQ_MSG(pfd[1].revents, POLLOUT|POLLWRNORM, - "pfd[1].revents=0x%x", pfd[1].revents); + "pfd[1].revents=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd[1].revents)); RL(close(wfd)); RL(close(rfd)); @@ -415,6 +482,7 @@ ATF_TC_BODY(fifo_hup1, tc) struct pollfd pfd; int rfd, wfd; int ret; + char eventbuf[128]; fifo_support(); @@ -431,8 +499,9 @@ ATF_TC_BODY(fifo_hup1, tc) RL(ret = poll(&pfd, 1, 0)); ATF_REQUIRE_EQ_MSG(ret, 1, "got: %d", ret); ATF_REQUIRE_EQ_MSG((pfd.revents & (POLLHUP|POLLOUT)), POLLHUP, - "revents=0x%x expected POLLHUP=0x%x but not POLLOUT=0x%x", - pfd.revents, POLLHUP, POLLOUT); + "revents=%s expected POLLHUP=0x%x but not POLLOUT=0x%x", + formatpollevents(eventbuf, sizeof(eventbuf), pfd.revents), + POLLHUP, POLLOUT); /* * Check that POLLHUP is cleared when a writer re-connects. @@ -467,6 +536,7 @@ ATF_TC_BODY(fifo_hup2, tc) pid_t pid; struct timespec ts1, ts2; int ret; + char eventbuf[128]; fifo_support(); @@ -501,8 +571,9 @@ ATF_TC_BODY(fifo_hup2, tc) (long long)ts2.tv_sec, ts2.tv_nsec); ATF_REQUIRE_EQ_MSG((pfd.revents & (POLLHUP|POLLOUT)), POLLHUP, - "revents=0x%x expected POLLHUP=0x%x but not POLLOUT=0x%x", - pfd.revents, POLLHUP, POLLOUT); + "revents=%s expected POLLHUP=0x%x but not POLLOUT=0x%x", + formatpollevents(eventbuf, sizeof(eventbuf), pfd.revents), + POLLHUP, POLLOUT); } ATF_TC_CLEANUP(fifo_hup2, tc) @@ -568,6 +639,7 @@ check_pollclosedpeer_delayed_write(int w { struct pollfd pfd = { .fd = writefd, .events = POLLOUT }; struct timespec start, end, delta; + char actbuf[128], expbuf[128]; int nfds; /* @@ -599,9 +671,11 @@ check_pollclosedpeer_delayed_write(int w ATF_CHECK_EQ_MSG(pfd.fd, writefd, "pfd.fd=%d writefd=%d", pfd.fd, writefd); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), expected, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, expected, POLLOUT, POLLHUP, POLLIN); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), expected), + POLLHUP, POLLIN, POLLOUT); /* * We should have slept at least 1sec. @@ -625,9 +699,11 @@ check_pollclosedpeer_delayed_write(int w ATF_CHECK_EQ_MSG(pfd.fd, writefd, "pfd.fd=%d writefd=%d", pfd.fd, writefd); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), expected, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, expected, POLLOUT, POLLHUP, POLLIN); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), expected), + POLLHUP, POLLIN, POLLOUT); } static void @@ -650,6 +726,7 @@ check_pollclosedpeer_delayed_read(int re struct pollfd pfd; struct timespec start, end, delta; int nfds; + char actbuf[128], expbuf[128]; /* * Don't let poll sleep for more than 3sec. (The close delay @@ -682,9 +759,11 @@ check_pollclosedpeer_delayed_read(int re pfd.fd, readfd, writefd); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), pollhup|POLLIN, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, pollhup|POLLIN, POLLHUP, POLLIN, POLLOUT); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), pollhup|POLLIN), + POLLHUP, POLLIN, POLLOUT); /* * We should have slept at least 1sec. @@ -710,9 +789,11 @@ check_pollclosedpeer_delayed_read(int re pfd.fd, readfd, writefd); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), pollhup|POLLIN, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, pollhup|POLLIN, POLLHUP, POLLIN, POLLOUT); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), pollhup|POLLIN), + POLLHUP, POLLIN, POLLOUT); } static void @@ -807,6 +888,7 @@ check_pollclosedpeer_immediate_write(int { struct pollfd pfd = { .fd = writefd, .events = POLLOUT }; int nfds; + char actbuf[128], expbuf[128]; /* * Close the reader side immediately. @@ -826,9 +908,11 @@ check_pollclosedpeer_immediate_write(int ATF_CHECK_EQ_MSG(pfd.fd, writefd, "pfd.fd=%d writefd=%d", pfd.fd, writefd); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), expected, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, expected, POLLOUT, POLLHUP, POLLIN); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), expected), + POLLHUP, POLLIN, POLLOUT); /* * Write should fail with EPIPE/SIGPIPE now -- and continue to @@ -843,6 +927,7 @@ check_pollclosedpeer_immediate_readnone( { struct pollfd pfd = { .fd = readfd, .events = POLLIN }; int nfds; + char actbuf[128], expbuf[128]; /* * Close the writer side immediately. @@ -861,9 +946,11 @@ check_pollclosedpeer_immediate_readnone( ATF_CHECK_EQ_MSG(nfds, 1, "nfds=%d", nfds); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), pollhup|POLLIN, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, pollhup|POLLIN, POLLHUP, POLLIN, POLLOUT); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), pollhup|POLLIN), + POLLHUP, POLLIN, POLLOUT); /* * Read should return EOF now -- and continue to do so. @@ -879,6 +966,7 @@ check_pollclosedpeer_immediate_readsome( char buf[BUFSIZ]; ssize_t nread; int nfds; + char actbuf[128], expbuf[128]; /* * Close the writer side immediately. @@ -898,9 +986,11 @@ check_pollclosedpeer_immediate_readsome( ATF_CHECK_EQ_MSG(nfds, 1, "nfds=%d", nfds); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), pollhup|POLLIN, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, pollhup|POLLIN, POLLHUP, POLLIN, POLLOUT); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), pollhup|POLLIN), + POLLHUP, POLLIN, POLLOUT); /* * Read all the data. Each read should complete instantly -- @@ -925,9 +1015,11 @@ check_pollclosedpeer_immediate_readsome( ATF_CHECK_EQ_MSG(nfds, 1, "nfds=%d", nfds); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), pollhup|POLLIN, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, pollhup|POLLIN, POLLHUP, POLLIN, POLLOUT); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), pollhup|POLLIN), + POLLHUP, POLLIN, POLLOUT); /* * Read should return EOF now -- and continue to do so. @@ -944,9 +1036,11 @@ check_pollclosedpeer_immediate_readsome( ATF_CHECK_EQ_MSG(nfds, 1, "nfds=%d", nfds); ATF_CHECK_EQ_MSG((pfd.revents & (POLLHUP|POLLIN|POLLOUT)), pollhup|POLLIN, - "revents=0x%x expected=0x%x" + "revents=%s expected=%s" " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", - pfd.revents, pollhup|POLLIN, POLLHUP, POLLIN, POLLOUT); + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), pollhup|POLLIN), + POLLHUP, POLLIN, POLLOUT); } static void * # HG changeset patch # User Taylor R Campbell # Date 1790177228 0 # Wed Sep 23 15:27:08 2026 +0000 # Branch trunk # Node ID 2947078f3a958cb0ad18689d7ea6abbd597931bb # Parent 95a742e755b0db88f8af792e8a6adaff484f62b6 # EXP-Topic riastradh-pr59056-pollhup t_poll: Test concurrently closing an fd being polled. PR kern/59056: poll POLLHUP bugs diff -r 95a742e755b0 -r 2947078f3a95 tests/lib/libc/sys/t_poll.c --- a/tests/lib/libc/sys/t_poll.c Tue Sep 22 20:01:14 2026 +0000 +++ b/tests/lib/libc/sys/t_poll.c Wed Sep 23 15:27:08 2026 +0000 @@ -2205,6 +2205,601 @@ ATF_TC_BODY(pollclosedpeer_socketpair1_d &check_pollclosedpeer_delayed_read_socket); } +struct pollclosedself_thread_ctx { + pthread_barrier_t bar; + int fd; +}; + +static void * +pollclosedself_thread(void *cookie) +{ + struct pollclosedself_thread_ctx *C = cookie; + + fprintf(stderr, "[thread] wait for barrier\n"); + (void)pthread_barrier_wait(&C->bar); + RL(usleep(1)); + fprintf(stderr, "[thread] closing fd\n"); + RL(close(C->fd)); + fprintf(stderr, "[thread] closed fd\n"); + return NULL; +} + +static void +check_pollclosedself_thread(int fd, int events) +{ + struct pollclosedself_thread_ctx ctx, *C = &ctx; + pthread_t t; + struct pollfd pfd; + int nfds; + char actbuf[128], expbuf[128]; + + C->fd = fd; + RZ(pthread_barrier_init(&C->bar, NULL, 2)); + RZ(pthread_create(&t, NULL, &pollclosedself_thread, C)); + + REQUIRE_LIBC(alarm(3), (unsigned)-1); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = events; + + fprintf(stderr, "wait for barrier and start first poll\n"); + (void)pthread_barrier_wait(&C->bar); + RL(nfds = poll(&pfd, 1, -1)); + fprintf(stderr, "first poll returned nfds=%d\n", nfds); + ATF_REQUIRE_EQ_MSG(nfds, 1, "nfds=%d", nfds); + fprintf(stderr, "first poll returned revents=%s\n", + formatpollevents(actbuf, sizeof(actbuf), pfd.revents)); + ATF_CHECK_EQ_MSG((pfd.revents & + (POLLIN|POLLOUT|POLLHUP|POLLERR|POLLNVAL)), + POLLNVAL, + "revents=%s expected=%s", + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), POLLNVAL)); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = events; + fprintf(stderr, "start second poll\n"); + RL(nfds = poll(&pfd, 1, -1)); + fprintf(stderr, "second poll returned nfds=%d\n", nfds); + ATF_REQUIRE_EQ_MSG(nfds, 1, "nfds=%d", nfds); + fprintf(stderr, "second poll returned revents=%s\n", + formatpollevents(actbuf, sizeof(actbuf), pfd.revents)); + ATF_CHECK_EQ_MSG((pfd.revents & + (POLLIN|POLLOUT|POLLHUP|POLLERR|POLLNVAL)), + POLLNVAL, + "revents=%s expected=%s", + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), POLLNVAL)); + + ATF_CHECK_ERRNO(EBADF, fcntl(fd, F_GETFL) == -1); + + RZ(pthread_join(t, NULL)); +} + +static void +on_sigusr1_close(int signo, siginfo_t *si, void *ctx) +{ + int fd; + + if (si->si_code != SI_QUEUE || si->si_pid != getpid()) { + fprintf(stderr, "[signal] spurious SIGUSR1," + " si_code=%d si_pid=%d si_user=%d\n", + si->si_code, (int)si->si_pid, (int)si->si_uid); + return; + } + fd = si->si_value.sival_int; + fprintf(stderr, "[signal] closing fd on SIGUSR1\n"); + RL(close(fd)); + fprintf(stderr, "[signal] closed fd\n"); +} + +static void +check_pollclosedself_signal(int fd, int events) +{ + struct sigaction sa = { + .sa_sigaction = &on_sigusr1_close, + .sa_flags = SA_SIGINFO|SA_RESTART, + }; + sigset_t omask; + struct pollfd pfd; + int nfds; + char eventbuf[128]; + + RL(sigemptyset(&sa.sa_mask)); + RL(sigaddset(&sa.sa_mask, SIGUSR1)); + RL(sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask)); + RL(sigaction(SIGUSR1, &sa, NULL)); + RL(sigqueue(getpid(), SIGUSR1, (union sigval){.sival_int = fd})); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = events; + ATF_CHECK_ERRNO(EINTR, ppoll(&pfd, 1, NULL, &omask) == -1); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = events; + RL(nfds = ppoll(&pfd, 1, NULL, &omask)); + fprintf(stderr, "second poll returned nfds=%d\n", nfds); + ATF_REQUIRE_EQ_MSG(nfds, 1, "nfds=%d", nfds); + fprintf(stderr, "second poll returned revents=%s\n", + formatpollevents(eventbuf, sizeof(eventbuf), pfd.revents)); + ATF_CHECK_EQ_MSG((pfd.revents & + (POLLIN|POLLOUT|POLLHUP|POLLERR|POLLNVAL)), + POLLNVAL, + "revents=%s expected=%s", + formatpollevents(eventbuf, sizeof(eventbuf), pfd.revents), + formatpollevents(eventbuf, sizeof(eventbuf), POLLNVAL)); + + ATF_CHECK_ERRNO(EBADF, fcntl(fd, F_GETFL) == -1); +} + +/* + * Cartesian product of: + * + * 1. [fifo0] first fifo opener + * 2. [fifo1] second fifo opener + * 3. [pipe] pipe + * 4. [ptyhost] host side of pty + * 5. [ptyapp] application side of pty + * 6. [socketpair0] first side of socket pair + * 7. [socketpair1] second side of socket pair + * + * with + * + * 1. [thread] close fd with a concurrent thread + * 2. [signal] close fd in a signal handler interrupting ppoll + * + * with (where applicable) + * + * 1. [read] waiting for read + * 2. [write] waiting for write + */ + +ATF_TC(pollclosedself_fifo0_thread_read); +ATF_TC_HEAD(pollclosedself_fifo0_thread_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_fifo0_thread_read, tc) +{ + int writefd, readfd; + + /* + * Similar bug but for named fifos instead. + */ + atf_tc_expect_signal(SIGALRM, "PR kern/57659:" + " closing pipe writefd fails to wake concurrent write on" + " same writefd"); + + pollclosed_fifo0_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_thread(readfd, POLLIN); +} + +ATF_TC(pollclosedself_fifo0_signal_read); +ATF_TC_HEAD(pollclosedself_fifo0_signal_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_fifo0_signal_read, tc) +{ + int writefd, readfd; + + pollclosed_fifo0_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_signal(readfd, POLLIN); +} + +ATF_TC(pollclosedself_fifo0_thread_write); +ATF_TC_HEAD(pollclosedself_fifo0_thread_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_fifo0_thread_write, tc) +{ + int writefd, readfd; + + pollclosed_fifo0_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_thread(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_fifo0_signal_write); +ATF_TC_HEAD(pollclosedself_fifo0_signal_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_fifo0_signal_write, tc) +{ + int writefd, readfd; + + pollclosed_fifo0_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_fifo1_thread_read); +ATF_TC_HEAD(pollclosedself_fifo1_thread_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_fifo1_thread_read, tc) +{ + int writefd, readfd; + + /* + * Similar bug but for named fifos instead. + */ + atf_tc_expect_signal(SIGALRM, "PR kern/57659:" + " closing pipe writefd fails to wake concurrent write on" + " same writefd"); + + pollclosed_fifo1_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_thread(readfd, POLLIN); +} + +ATF_TC(pollclosedself_fifo1_signal_read); +ATF_TC_HEAD(pollclosedself_fifo1_signal_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_fifo1_signal_read, tc) +{ + int writefd, readfd; + + pollclosed_fifo1_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_signal(readfd, POLLIN); +} + +ATF_TC(pollclosedself_fifo1_thread_write); +ATF_TC_HEAD(pollclosedself_fifo1_thread_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_fifo1_thread_write, tc) +{ + int writefd, readfd; + + pollclosed_fifo1_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_thread(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_fifo1_signal_write); +ATF_TC_HEAD(pollclosedself_fifo1_signal_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_fifo1_signal_write, tc) +{ + int writefd, readfd; + + pollclosed_fifo1_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_pipe_thread_read); +ATF_TC_HEAD(pollclosedself_pipe_thread_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_pipe_thread_read, tc) +{ + int writefd, readfd; + + pollclosed_pipe_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_thread(readfd, POLLIN); +} + +ATF_TC(pollclosedself_pipe_signal_read); +ATF_TC_HEAD(pollclosedself_pipe_signal_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_pipe_signal_read, tc) +{ + int writefd, readfd; + + pollclosed_pipe_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_signal(readfd, POLLIN); +} + +ATF_TC(pollclosedself_pipe_thread_write); +ATF_TC_HEAD(pollclosedself_pipe_thread_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_pipe_thread_write, tc) +{ + int writefd, readfd; + + pollclosed_pipe_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_thread(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_pipe_signal_write); +ATF_TC_HEAD(pollclosedself_pipe_signal_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_pipe_signal_write, tc) +{ + int writefd, readfd; + + pollclosed_pipe_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_ptyhost_thread_read); +ATF_TC_HEAD(pollclosedself_ptyhost_thread_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_ptyhost_thread_read, tc) +{ + int writefd, readfd; + + pollclosed_ptyhost_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_thread(readfd, POLLIN); +} + +ATF_TC(pollclosedself_ptyhost_signal_read); +ATF_TC_HEAD(pollclosedself_ptyhost_signal_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_ptyhost_signal_read, tc) +{ + int writefd, readfd; + + pollclosed_ptyhost_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_signal(readfd, POLLIN); +} + +ATF_TC(pollclosedself_ptyhost_thread_write); +ATF_TC_HEAD(pollclosedself_ptyhost_thread_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_ptyhost_thread_write, tc) +{ + int writefd, readfd; + + /* + * Similar bug but for the host side of a pty instead (but + * curiously, not the application side of a pty!). + */ + atf_tc_expect_signal(SIGALRM, "PR kern/57659:" + " closing pipe writefd fails to wake concurrent write on" + " same writefd"); + + pollclosed_ptyhost_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_thread(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_ptyhost_signal_write); +ATF_TC_HEAD(pollclosedself_ptyhost_signal_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_ptyhost_signal_write, tc) +{ + int writefd, readfd; + + pollclosed_ptyhost_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_ptyapp_thread_read); +ATF_TC_HEAD(pollclosedself_ptyapp_thread_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_ptyapp_thread_read, tc) +{ + int writefd, readfd; + + pollclosed_ptyapp_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_thread(readfd, POLLIN); +} + +ATF_TC(pollclosedself_ptyapp_signal_read); +ATF_TC_HEAD(pollclosedself_ptyapp_signal_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_ptyapp_signal_read, tc) +{ + int writefd, readfd; + + pollclosed_ptyapp_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_signal(readfd, POLLIN); +} + +ATF_TC(pollclosedself_ptyapp_thread_write); +ATF_TC_HEAD(pollclosedself_ptyapp_thread_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_ptyapp_thread_write, tc) +{ + int writefd, readfd; + + pollclosed_ptyapp_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_thread(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_ptyapp_signal_write); +ATF_TC_HEAD(pollclosedself_ptyapp_signal_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_ptyapp_signal_write, tc) +{ + int writefd, readfd; + + pollclosed_ptyapp_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_socketpair0_thread_read); +ATF_TC_HEAD(pollclosedself_socketpair0_thread_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_socketpair0_thread_read, tc) +{ + int writefd, readfd; + + pollclosed_socketpair0_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_thread(readfd, POLLIN); +} + +ATF_TC(pollclosedself_socketpair0_signal_read); +ATF_TC_HEAD(pollclosedself_socketpair0_signal_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_socketpair0_signal_read, tc) +{ + int writefd, readfd; + + pollclosed_socketpair0_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_signal(readfd, POLLIN); +} + +ATF_TC(pollclosedself_socketpair0_thread_write); +ATF_TC_HEAD(pollclosedself_socketpair0_thread_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_socketpair0_thread_write, tc) +{ + int writefd, readfd; + + pollclosed_socketpair0_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_thread(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_socketpair0_signal_write); +ATF_TC_HEAD(pollclosedself_socketpair0_signal_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_socketpair0_signal_write, tc) +{ + int writefd, readfd; + + pollclosed_socketpair0_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_socketpair1_thread_read); +ATF_TC_HEAD(pollclosedself_socketpair1_thread_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_socketpair1_thread_read, tc) +{ + int writefd, readfd; + + pollclosed_socketpair1_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_thread(readfd, POLLIN); +} + +ATF_TC(pollclosedself_socketpair1_signal_read); +ATF_TC_HEAD(pollclosedself_socketpair1_signal_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_socketpair1_signal_read, tc) +{ + int writefd, readfd; + + pollclosed_socketpair1_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedself_signal(readfd, POLLIN); +} + +ATF_TC(pollclosedself_socketpair1_thread_write); +ATF_TC_HEAD(pollclosedself_socketpair1_thread_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a thread"); +} +ATF_TC_BODY(pollclosedself_socketpair1_thread_write, tc) +{ + int writefd, readfd; + + pollclosed_socketpair1_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_thread(writefd, POLLOUT); +} + +ATF_TC(pollclosedself_socketpair1_signal_write); +ATF_TC_HEAD(pollclosedself_socketpair1_signal_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Checks poll on an fd concurrently closed by a signal handler"); +} +ATF_TC_BODY(pollclosedself_socketpair1_signal_write, tc) +{ + int writefd, readfd; + + pollclosed_socketpair1_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); +} + ATF_TP_ADD_TCS(tp) { @@ -2280,5 +2875,40 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair0_delayed_thread_read); ATF_TP_ADD_TC(tp, pollclosedpeer_socketpair1_delayed_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_fifo0_signal_read); + ATF_TP_ADD_TC(tp, pollclosedself_fifo0_signal_write); + ATF_TP_ADD_TC(tp, pollclosedself_fifo0_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_fifo0_thread_write); + + ATF_TP_ADD_TC(tp, pollclosedself_fifo1_signal_read); + ATF_TP_ADD_TC(tp, pollclosedself_fifo1_signal_write); + ATF_TP_ADD_TC(tp, pollclosedself_fifo1_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_fifo1_thread_write); + + ATF_TP_ADD_TC(tp, pollclosedself_pipe_signal_read); + ATF_TP_ADD_TC(tp, pollclosedself_pipe_signal_write); + ATF_TP_ADD_TC(tp, pollclosedself_pipe_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_pipe_thread_write); + + ATF_TP_ADD_TC(tp, pollclosedself_ptyhost_signal_read); + ATF_TP_ADD_TC(tp, pollclosedself_ptyhost_signal_write); + ATF_TP_ADD_TC(tp, pollclosedself_ptyhost_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_ptyhost_thread_write); + + ATF_TP_ADD_TC(tp, pollclosedself_ptyapp_signal_read); + ATF_TP_ADD_TC(tp, pollclosedself_ptyapp_signal_write); + ATF_TP_ADD_TC(tp, pollclosedself_ptyapp_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_ptyapp_thread_write); + + ATF_TP_ADD_TC(tp, pollclosedself_socketpair0_signal_read); + ATF_TP_ADD_TC(tp, pollclosedself_socketpair0_signal_write); + ATF_TP_ADD_TC(tp, pollclosedself_socketpair0_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_socketpair0_thread_write); + + ATF_TP_ADD_TC(tp, pollclosedself_socketpair1_signal_read); + ATF_TP_ADD_TC(tp, pollclosedself_socketpair1_signal_write); + ATF_TP_ADD_TC(tp, pollclosedself_socketpair1_thread_read); + ATF_TP_ADD_TC(tp, pollclosedself_socketpair1_thread_write); + return atf_no_error(); } # HG changeset patch # User Taylor R Campbell # Date 1790107552 0 # Tue Sep 22 20:05:52 2026 +0000 # Branch trunk # Node ID 46f3a9179ae759a8c4f965a882a58fa807b20f65 # Parent 2947078f3a958cb0ad18689d7ea6abbd597931bb # EXP-Topic riastradh-pr59056-pollhup pipe(2): Rename pipe variables to make more sense. - wpipe for the writer side of a pipe, associated with a file open for FWRITE. - rpipe for the writer side of a pipe, associated with a file open for FREAD. - pipe for either side of the pipe. - ppipe for the peer corresponding to pipe. No functional change intended. Prompted by trying to wrap my head around this pipe(2) code in order to address: PR kern/59056: poll POLLHUP bugs diff -r 2947078f3a95 -r 46f3a9179ae7 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 15:27:08 2026 +0000 +++ b/sys/kern/sys_pipe.c Tue Sep 22 20:05:52 2026 +0000 @@ -571,34 +571,34 @@ pipe_write(file_t *fp, off_t *offset, st unsigned int wakeup_state = 0; /* We want to write to our peer */ - rpipe = fp->f_pipe; - lock = rpipe->pipe_lock; + wpipe = fp->f_pipe; + lock = wpipe->pipe_lock; error = 0; mutex_enter(lock); - wpipe = rpipe->pipe_peer; + rpipe = wpipe->pipe_peer; /* * Detect loss of pipe read side, issue SIGPIPE if lost. */ - if (wpipe == NULL || (wpipe->pipe_state & PIPE_EOF) != 0) { + if (rpipe == NULL || (rpipe->pipe_state & PIPE_EOF) != 0) { mutex_exit(lock); return EPIPE; } - ++wpipe->pipe_busy; + ++rpipe->pipe_busy; /* Acquire the long-term pipe lock */ - if ((error = pipelock(wpipe, true)) != 0) { - --wpipe->pipe_busy; - if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&wpipe->pipe_draincv); + if ((error = pipelock(rpipe, true)) != 0) { + --rpipe->pipe_busy; + if (rpipe->pipe_busy == 0) { + rpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&rpipe->pipe_draincv); } mutex_exit(lock); return (error); } - bp = &wpipe->pipe_buffer; + bp = &rpipe->pipe_buffer; /* * If it is advantageous to resize the pipe buffer, do so. @@ -607,7 +607,7 @@ pipe_write(file_t *fp, off_t *offset, st (nbigpipe < maxbigpipes) && (bp->size <= PIPE_SIZE) && (bp->cnt == 0)) { - if (pipespace(wpipe, BIG_PIPE_SIZE) == 0) + if (pipespace(rpipe, BIG_PIPE_SIZE) == 0) atomic_inc_uint(&nbigpipe); } @@ -675,7 +675,7 @@ pipe_write(file_t *fp, off_t *offset, st /* * If the "read-side" has been blocked, wake it up now. */ - cv_broadcast(&wpipe->pipe_rcv); + cv_broadcast(&rpipe->pipe_rcv); /* * Don't block on non-blocking I/O. @@ -690,7 +690,7 @@ pipe_write(file_t *fp, off_t *offset, st * wake up select/poll. */ if (bp->cnt) - pipeselwakeup(wpipe, wpipe, POLL_IN); + pipeselwakeup(rpipe, rpipe, POLL_IN); if (wakeup_state & PIPE_RESTART) { error = ERESTART; @@ -701,27 +701,27 @@ pipe_write(file_t *fp, off_t *offset, st * If read side wants to go away, we just issue a signal * to ourselves. */ - if (wpipe->pipe_state & PIPE_EOF) { + if (rpipe->pipe_state & PIPE_EOF) { error = EPIPE; break; } - pipeunlock(wpipe); - error = cv_wait_sig(&wpipe->pipe_wcv, lock); - (void)pipelock(wpipe, false); + pipeunlock(rpipe); + error = cv_wait_sig(&rpipe->pipe_wcv, lock); + (void)pipelock(rpipe, false); if (error != 0) break; - wakeup_state = wpipe->pipe_state; + wakeup_state = rpipe->pipe_state; } } - --wpipe->pipe_busy; - if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&wpipe->pipe_draincv); + --rpipe->pipe_busy; + if (rpipe->pipe_busy == 0) { + rpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&rpipe->pipe_draincv); } if (bp->cnt > 0) { - cv_broadcast(&wpipe->pipe_rcv); + cv_broadcast(&rpipe->pipe_rcv); } /* @@ -731,20 +731,20 @@ pipe_write(file_t *fp, off_t *offset, st error = 0; if (error == 0) - getnanotime(&wpipe->pipe_mtime); + getnanotime(&rpipe->pipe_mtime); /* * We have something to offer, wake up select/poll. */ if (bp->cnt) - pipeselwakeup(wpipe, wpipe, POLL_IN); + pipeselwakeup(rpipe, rpipe, POLL_IN); /* * Arrange for next read(2) to do a signal. */ - wpipe->pipe_state |= PIPE_SIGNALR; + rpipe->pipe_state |= PIPE_SIGNALR; - pipeunlock(wpipe); + pipeunlock(rpipe); mutex_exit(lock); return (error); } @@ -817,43 +817,43 @@ pipe_ioctl(file_t *fp, u_long cmd, void int pipe_poll(file_t *fp, int events) { - struct pipe *rpipe = fp->f_pipe; - struct pipe *wpipe; + struct pipe *pipe = fp->f_pipe; + struct pipe *ppipe; int eof = 0; int revents = 0; - mutex_enter(rpipe->pipe_lock); - wpipe = rpipe->pipe_peer; + mutex_enter(pipe->pipe_lock); + ppipe = pipe->pipe_peer; if (events & (POLLIN | POLLRDNORM)) - if ((rpipe->pipe_buffer.cnt > 0) || - (rpipe->pipe_state & PIPE_EOF)) + if ((pipe->pipe_buffer.cnt > 0) || + (pipe->pipe_state & PIPE_EOF)) revents |= events & (POLLIN | POLLRDNORM); - eof |= (rpipe->pipe_state & PIPE_EOF); + eof |= (pipe->pipe_state & PIPE_EOF); - if (wpipe == NULL) + if (ppipe == NULL) revents |= events & (POLLOUT | POLLWRNORM); else { if (events & (POLLOUT | POLLWRNORM)) - if ((wpipe->pipe_state & PIPE_EOF) || ( - (wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt) >= PIPE_BUF)) + if ((ppipe->pipe_state & PIPE_EOF) || ( + (ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt) >= PIPE_BUF)) revents |= events & (POLLOUT | POLLWRNORM); - eof |= (wpipe->pipe_state & PIPE_EOF); + eof |= (ppipe->pipe_state & PIPE_EOF); } - if (wpipe == NULL || eof) + if (ppipe == NULL || eof) revents |= POLLHUP; if (revents == 0) { if (events & (POLLIN | POLLRDNORM)) - selrecord(curlwp, &rpipe->pipe_sel); + selrecord(curlwp, &pipe->pipe_sel); if (events & (POLLOUT | POLLWRNORM)) - selrecord(curlwp, &wpipe->pipe_sel); + selrecord(curlwp, &ppipe->pipe_sel); } - mutex_exit(rpipe->pipe_lock); + mutex_exit(pipe->pipe_lock); return (revents); } @@ -1065,18 +1065,18 @@ filt_pipedetach(struct knote *kn) static int filt_piperead(struct knote *kn, long hint) { - struct pipe *rpipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *wpipe; + struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *ppipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(rpipe->pipe_lock); + mutex_enter(pipe->pipe_lock); } - wpipe = rpipe->pipe_peer; - kn->kn_data = rpipe->pipe_buffer.cnt; + ppipe = pipe->pipe_peer; + kn->kn_data = pipe->pipe_buffer.cnt; - if ((rpipe->pipe_state & PIPE_EOF) || - (wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { + if ((pipe->pipe_state & PIPE_EOF) || + (ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { knote_set_eof(kn, 0); rv = 1; } else { @@ -1084,7 +1084,7 @@ filt_piperead(struct knote *kn, long hin } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(rpipe->pipe_lock); + mutex_exit(pipe->pipe_lock); } return rv; } @@ -1092,26 +1092,26 @@ filt_piperead(struct knote *kn, long hin static int filt_pipewrite(struct knote *kn, long hint) { - struct pipe *rpipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *wpipe; + struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *ppipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(rpipe->pipe_lock); + mutex_enter(pipe->pipe_lock); } - wpipe = rpipe->pipe_peer; + ppipe = pipe->pipe_peer; - if ((wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { + if ((ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { kn->kn_data = 0; knote_set_eof(kn, 0); rv = 1; } else { - kn->kn_data = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt; + kn->kn_data = ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt; rv = kn->kn_data >= PIPE_BUF; } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(rpipe->pipe_lock); + mutex_exit(pipe->pipe_lock); } return rv; } # HG changeset patch # User Taylor R Campbell # Date 1790118143 0 # Tue Sep 22 23:02:23 2026 +0000 # Branch trunk # Node ID 5d1b3f1e8722a23bf58eb31f977476eeb68c1b19 # Parent 46f3a9179ae759a8c4f965a882a58fa807b20f65 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Split new function pipefree out of pipeclose. Makes it easier to reason about pipeclose this way. Prompted by: PR kern/59056: poll POLLHUP bugs diff -r 46f3a9179ae7 -r 5d1b3f1e8722 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Tue Sep 22 20:05:52 2026 +0000 +++ b/sys/kern/sys_pipe.c Tue Sep 22 23:02:23 2026 +0000 @@ -127,6 +127,7 @@ static u_int nbigpipe = 0; static u_int amountpipekva = 0; static void pipeclose(struct pipe *); +static void pipefree(struct pipe *); static void pipe_free_kmem(struct pipe *); static int pipe_create(struct pipe **, pool_cache_t, struct timespec *); static int pipelock(struct pipe *, bool); @@ -258,8 +259,10 @@ pipe1(struct lwp *l, int *fildes, int fl free3: fd_abort(p, rf, fildes[0]); free2: - pipeclose(wpipe); - pipeclose(rpipe); + if (wpipe) + pipefree(wpipe); + if (rpipe) + pipefree(rpipe); return (error); } @@ -962,18 +965,13 @@ pipeclose(struct pipe *pipe) kmutex_t *lock; struct pipe *ppipe; - if (pipe == NULL) - return; - KASSERT(cv_is_valid(&pipe->pipe_rcv)); KASSERT(cv_is_valid(&pipe->pipe_wcv)); KASSERT(cv_is_valid(&pipe->pipe_draincv)); KASSERT(cv_is_valid(&pipe->pipe_lkcv)); lock = pipe->pipe_lock; - if (lock == NULL) - /* Must have failed during create */ - goto free_resources; + KASSERT(lock != NULL); mutex_enter(lock); pipeselwakeup(pipe, pipe, POLL_HUP); @@ -1016,7 +1014,13 @@ pipeclose(struct pipe *pipe) /* * Free resources. */ - free_resources: + pipefree(pipe); +} + +static void +pipefree(struct pipe *pipe) +{ + pipe->pipe_pgid = 0; pipe->pipe_state = PIPE_SIGNALR; pipe->pipe_peer = NULL; # HG changeset patch # User Taylor R Campbell # Date 1790123630 0 # Wed Sep 23 00:33:50 2026 +0000 # Branch trunk # Node ID 09bbea7d457c23c0cd614275fad9b24c509e0b76 # Parent 5d1b3f1e8722a23bf58eb31f977476eeb68c1b19 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Fix wakeup of pending writers on close of write side. The job of pipe_restart is to wake any pending I/O operations on the file when it is about to be closed. New references cannot be taken for new I/O operations; once all existing references are drained, the system calls pipe_close. What pipe_restart did was to wake pipe->pipe_rcv and pipe->pipe_wcv. But the condvars of _which pipe_? After renaming the variables to match reality, it becomes clear that wpipe->pipe_wcv and wpipe->pipe_rcv are never used -- instead, pipe_read waits for rpipe->pipe_rcv, and pipe_write waits for rpipe->pipe_wcv. So pipe_restart on the write side of a pipe woke wpipe->pipe_rcv and wpipe->pipe_wcv, which nothing was waiting for, and failed to wake rpipe->pipe_rcv or rpipe->pipe_wcv. (Perhaps we should just have a single struct pipe::pipe_cv member, and have pipe_read use rpipe->pipe_cv and pipe_write use wpipe->pipe_cv. But that will be left for future cleanup.) PR kern/57659: closing pipe writefd fails to wake concurrent write on same writefd diff -r 5d1b3f1e8722 -r 09bbea7d457c sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Tue Sep 22 23:02:23 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 00:33:50 2026 +0000 @@ -902,6 +902,7 @@ static void pipe_restart(file_t *fp) { struct pipe *pipe = fp->f_pipe; + struct pipe *rpipe; /* * Unblock blocked reads/writes in order to allow close() to complete. @@ -909,11 +910,12 @@ pipe_restart(file_t *fp) * (Partial writes return the transfer length.) */ mutex_enter(pipe->pipe_lock); - pipe->pipe_state |= PIPE_RESTART; - /* Wakeup both cvs, maybe we only need one, but maybe there are some - * other paths where wakeup is needed, and it saves deciding which! */ - cv_broadcast(&pipe->pipe_rcv); - cv_broadcast(&pipe->pipe_wcv); + rpipe = (fp->f_flag & FREAD) ? pipe : pipe->pipe_peer; + if (rpipe != NULL) { + rpipe->pipe_state |= PIPE_RESTART; + cv_broadcast(&rpipe->pipe_rcv); + cv_broadcast(&rpipe->pipe_wcv); + } mutex_exit(pipe->pipe_lock); } diff -r 5d1b3f1e8722 -r 09bbea7d457c tests/kernel/t_fdrestart.c --- a/tests/kernel/t_fdrestart.c Tue Sep 22 23:02:23 2026 +0000 +++ b/tests/kernel/t_fdrestart.c Wed Sep 23 00:33:50 2026 +0000 @@ -218,7 +218,6 @@ ATF_TC_BODY(pipe_write, tc) memset(F, 0, sizeof(*F)); F->op = &dowrite; F->fd = fd[1]; - atf_tc_expect_fail("PR kern/57659"); testfdrestart(F); } # HG changeset patch # User Taylor R Campbell # Date 1790127572 0 # Wed Sep 23 01:39:32 2026 +0000 # Branch trunk # Node ID 9b907d8a710e9196157463a7465dbe58b8b9054b # Parent 09bbea7d457c23c0cd614275fad9b24c509e0b76 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Make pipe sides a little more symmetric. 1. When reading from or writing to a pipe, busy _this side_ of the pipe, not the other side. 2. In pipeclose, all operations on _this side_ of the pipe have already quiesced. But operations on the other side may not have. So wait for the _other side_ to be unbusied before disconnecting the peer (changing ppipe->pipe_peer from pipe to NULL). With (1) and (2) we can prove a simple property that makes reasoning about this code easier: If a pipe is busy, its peer pointer is stable even across cv_wait on the pipe lock. Without these changes I'm not sure I could prove that property (though I haven't exhibited a reproducer for any issues arising from its failure). 3. Make write wait on wpipe->pipe_wcv, and make read wait on rpipe->pipe_rcv, consistently, so that any waiters on one side of a pipe will always be waiting on one of _that side's_ condvars. This makes the logic in pipe_restart to wake any pending operations on _this side_ simpler -- it doesn't have to reach over to the peer, and it doesn't have to inadvertently wake the peer for no reason. In fact this brings the text of the code in pipe_restart back to what it was before I renamed the variables to reflect their actual content, suggesting there has been some confusion from the variable naming over the years... (In a future change, I would like to merge pipe_rcv and pipe_wcv into a single condvar per side -- no need to initialize two extra condvars per pipe that will never be used!) Preparation for: PR kern/59056: poll POLLHUP bugs diff -r 09bbea7d457c -r 9b907d8a710e sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 00:33:50 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 01:39:32 2026 +0000 @@ -411,6 +411,7 @@ pipe_read(file_t *fp, off_t *offset, str int flags) { struct pipe *rpipe = fp->f_pipe; + struct pipe *wpipe; struct pipebuf *bp = &rpipe->pipe_buffer; kmutex_t *lock = rpipe->pipe_lock; int error; @@ -490,6 +491,9 @@ again: /* * Detect EOF condition. * Read returns 0 on EOF, no need to set error. + * + * XXX Why rpipe->pipe_state and not wpipe->pipe_state? + * XXX Distinguish reader-closed from writer-closed? */ if (rpipe->pipe_state & PIPE_EOF) break; @@ -509,7 +513,6 @@ again: */ pipeunlock(rpipe); -#if 1 /* XXX (dsl) I'm sure these aren't needed here ... */ /* * We want to read more, wake up select/poll. */ @@ -518,8 +521,8 @@ again: /* * If the "write-side" is blocked, wake it up now. */ - cv_broadcast(&rpipe->pipe_wcv); -#endif + wpipe = rpipe->pipe_peer; + cv_broadcast(&wpipe->pipe_wcv); if (wakeup_state & PIPE_RESTART) { error = ERESTART; @@ -545,7 +548,8 @@ unlocked_error: cv_broadcast(&rpipe->pipe_draincv); } if (bp->cnt < MINPIPESIZE) { - cv_broadcast(&rpipe->pipe_wcv); + if ((wpipe = rpipe->pipe_peer) != NULL) + cv_broadcast(&wpipe->pipe_wcv); } /* @@ -588,14 +592,14 @@ pipe_write(file_t *fp, off_t *offset, st mutex_exit(lock); return EPIPE; } - ++rpipe->pipe_busy; + ++wpipe->pipe_busy; /* Acquire the long-term pipe lock */ if ((error = pipelock(rpipe, true)) != 0) { - --rpipe->pipe_busy; - if (rpipe->pipe_busy == 0) { - rpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&rpipe->pipe_draincv); + --wpipe->pipe_busy; + if (wpipe->pipe_busy == 0) { + wpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&wpipe->pipe_draincv); } mutex_exit(lock); return (error); @@ -703,6 +707,11 @@ pipe_write(file_t *fp, off_t *offset, st /* * If read side wants to go away, we just issue a signal * to ourselves. + * + * XXX Shouldn't this happen before we uiomove anything? + * + * XXX Why rpipe->pipe_state and not wpipe->pipe_state? + * XXX Distinguish reader-closed from writer-closed? */ if (rpipe->pipe_state & PIPE_EOF) { error = EPIPE; @@ -710,18 +719,18 @@ pipe_write(file_t *fp, off_t *offset, st } pipeunlock(rpipe); - error = cv_wait_sig(&rpipe->pipe_wcv, lock); + error = cv_wait_sig(&wpipe->pipe_wcv, lock); (void)pipelock(rpipe, false); if (error != 0) break; - wakeup_state = rpipe->pipe_state; + wakeup_state = wpipe->pipe_state; } } - --rpipe->pipe_busy; - if (rpipe->pipe_busy == 0) { - rpipe->pipe_state &= ~PIPE_RESTART; - cv_broadcast(&rpipe->pipe_draincv); + --wpipe->pipe_busy; + if (wpipe->pipe_busy == 0) { + wpipe->pipe_state &= ~PIPE_RESTART; + cv_broadcast(&wpipe->pipe_draincv); } if (bp->cnt > 0) { cv_broadcast(&rpipe->pipe_rcv); @@ -729,6 +738,11 @@ pipe_write(file_t *fp, off_t *offset, st /* * Don't return EPIPE if I/O was successful + * + * XXX Shouldn't we avoid returning _any_ error if we + * transmitted _any_ positive number of bytes? Or does that + * happen downstream of here, and if so, why do we need to do + * that here? */ if (error == EPIPE && bp->cnt == 0 && uio->uio_resid == 0) error = 0; @@ -902,7 +916,6 @@ static void pipe_restart(file_t *fp) { struct pipe *pipe = fp->f_pipe; - struct pipe *rpipe; /* * Unblock blocked reads/writes in order to allow close() to complete. @@ -910,12 +923,14 @@ pipe_restart(file_t *fp) * (Partial writes return the transfer length.) */ mutex_enter(pipe->pipe_lock); - rpipe = (fp->f_flag & FREAD) ? pipe : pipe->pipe_peer; - if (rpipe != NULL) { - rpipe->pipe_state |= PIPE_RESTART; - cv_broadcast(&rpipe->pipe_rcv); - cv_broadcast(&rpipe->pipe_wcv); - } + pipe->pipe_state |= PIPE_RESTART; + /* + * At most one of these is in use at any time, depending on + * whether fp->f_flag has FREAD or FWRITE set, but there's no + * harm in waking both here. + */ + cv_broadcast(&pipe->pipe_rcv); + cv_broadcast(&pipe->pipe_wcv); mutex_exit(pipe->pipe_lock); } @@ -979,24 +994,32 @@ pipeclose(struct pipe *pipe) pipeselwakeup(pipe, pipe, POLL_HUP); /* - * If the other side is blocked, wake it up saying that - * we want to close it down. + * fd_close has issued .fo_restart to wake all waiters on this + * side of the pipe, blocked new references, and waited for all + * references to drain, so it should not be possible for there + * to be any waiters remaining. (Only one of the condvars was + * ever in use anyway depending on whether this is the reader + * side or the writer side of the pipe.) + */ + KASSERT(!cv_has_waiters(&pipe->pipe_rcv)); + KASSERT(!cv_has_waiters(&pipe->pipe_wcv)); + + /* + * If the other side is busy, wake it up saying that + * we want to close it down, which will prevent peers + * from starting new I/O. Once it is no longer busy, + * disconnect it. */ pipe->pipe_state |= PIPE_EOF; - if (pipe->pipe_busy) { - while (pipe->pipe_busy) { - cv_broadcast(&pipe->pipe_wcv); - cv_wait_sig(&pipe->pipe_draincv, lock); - } - } - - /* - * Disconnect from peer. - */ if ((ppipe = pipe->pipe_peer) != NULL) { pipeselwakeup(ppipe, ppipe, POLL_HUP); ppipe->pipe_state |= PIPE_EOF; - cv_broadcast(&ppipe->pipe_rcv); + if (ppipe->pipe_busy) { + cv_broadcast(&ppipe->pipe_rcv); + cv_broadcast(&ppipe->pipe_wcv); + while (ppipe->pipe_busy) + cv_wait_sig(&ppipe->pipe_draincv, lock); + } ppipe->pipe_peer = NULL; } # HG changeset patch # User Taylor R Campbell # Date 1790127869 0 # Wed Sep 23 01:44:29 2026 +0000 # Branch trunk # Node ID 3ed9f1b9f02258f2a2262d6395eadb82fe1102d6 # Parent 9b907d8a710e9196157463a7465dbe58b8b9054b # EXP-Topic riastradh-pr59056-pollhup pipe(2): Simplify PIPE_RESTART handling. Now that it applies to each side independently, we can just make it final, because new I/O operations are not allowed on an file that has had its .fo_restart called. Makes reasoning about all this easier. XXX Consider renaming PIPE_RESTART to PIPE_CLOSING: the .fo_restart operation means the file is irreversibly destined to be closed soon and just needs any pending I/O on it to be interrupted and fail promptly so we can finally close the file. Preparation for: PR kern/59056: poll POLLHUP bugs diff -r 9b907d8a710e -r 3ed9f1b9f022 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 01:39:32 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 01:44:29 2026 +0000 @@ -544,7 +544,6 @@ again: unlocked_error: --rpipe->pipe_busy; if (rpipe->pipe_busy == 0) { - rpipe->pipe_state &= ~PIPE_RESTART; cv_broadcast(&rpipe->pipe_draincv); } if (bp->cnt < MINPIPESIZE) { @@ -598,7 +597,6 @@ pipe_write(file_t *fp, off_t *offset, st if ((error = pipelock(rpipe, true)) != 0) { --wpipe->pipe_busy; if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; cv_broadcast(&wpipe->pipe_draincv); } mutex_exit(lock); @@ -729,7 +727,6 @@ pipe_write(file_t *fp, off_t *offset, st --wpipe->pipe_busy; if (wpipe->pipe_busy == 0) { - wpipe->pipe_state &= ~PIPE_RESTART; cv_broadcast(&wpipe->pipe_draincv); } if (bp->cnt > 0) { # HG changeset patch # User Taylor R Campbell # Date 1790177677 0 # Wed Sep 23 15:34:37 2026 +0000 # Branch trunk # Node ID ba133cff7ddb13724e296ad3ccec5f892b19e13e # Parent 3ed9f1b9f02258f2a2262d6395eadb82fe1102d6 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Restructure select/poll/kqueue records. 1. When a thread is waiting on an endpoint of a pipe, have it wait on _that endpoint_ (i.e., cv_wait or selrecord), not on the other endpoint sometimes depending on the direction of the I/O. 2. Make poll(2) on the writer side of a pipe wake with POLLERR when the reader side is closed, because write would return immediately, and fail with EPIPE/SIGPIPE. See also: https://mail-index.NetBSD.org/tech-kern/2026/09/21/msg031255.html (In contrast, for the other way around, when the writer side of a pipe is closed, poll(2) on the reader is already defined to wake with POLLHUP, and while read would return immediately, it will not _fail_; it will simply report EOF, so there is no justification for POLLERR in that direction.) 3. For EVFILT_READ, require the reader side of a pipe; likewise, for EVFILT_WRITE, require the writer side of a pipe. That this wasn't enforced before was an accident, as far as I can tell, and made the kevent paths very confusing about which side of the pipe is which. PR kern/59056: poll POLLHUP bugs diff -r 3ed9f1b9f022 -r ba133cff7ddb sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 01:44:29 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 15:34:37 2026 +0000 @@ -126,13 +126,13 @@ static u_int nbigpipe = 0; */ static u_int amountpipekva = 0; -static void pipeclose(struct pipe *); +static void pipeclose(struct file *, struct pipe *); static void pipefree(struct pipe *); static void pipe_free_kmem(struct pipe *); static int pipe_create(struct pipe **, pool_cache_t, struct timespec *); static int pipelock(struct pipe *, bool); static inline void pipeunlock(struct pipe *); -static void pipeselwakeup(struct pipe *, struct pipe *, int); +static void pipeselwakeup(struct pipe *, int); static int pipespace(struct pipe *, int); static int pipe_ctor(void *, void *, int); static void pipe_dtor(void *, void *); @@ -369,14 +369,28 @@ pipeunlock(struct pipe *pipe) } /* - * Select/poll wakeup. This also sends SIGIO to peer connected to - * 'sigpipe' side of pipe. + * pipeselwakeup(pipe, code) + * + * Activity has happened on pipe's peer oncausing I/O to be + * available on pipe, so: + * + * 1. Wake any threads waiting in select/poll on pipe. + * + * 2. Deliver SIGIO to any process (group) configured to receive + * notifications about I/O on pipe. + * + * `code' is a siginfo_t si_code value in the POLL_* namespace for + * the type of notification the waiters will receive, and it + * should match the direction of the pipe -- POLL_OUT/POLL_ERR + * with the writer side, POLL_IN/POLL_HUP with the reader side. */ static void -pipeselwakeup(struct pipe *selp, struct pipe *sigp, int code) +pipeselwakeup(struct pipe *pipe, int code) { int band; + KASSERT(mutex_owned(pipe->pipe_lock)); + switch (code) { case POLL_IN: band = POLLIN|POLLRDNORM; @@ -398,12 +412,12 @@ pipeselwakeup(struct pipe *selp, struct break; } - selnotify(&selp->pipe_sel, band, NOTE_SUBMIT); + selnotify(&pipe->pipe_sel, band, NOTE_SUBMIT); - if (sigp == NULL || (sigp->pipe_state & PIPE_ASYNC) == 0) + if ((pipe->pipe_state & PIPE_ASYNC) == 0) return; - fownsignal(sigp->pipe_pgid, SIGIO, code, band, selp); + fownsignal(pipe->pipe_pgid, SIGIO, code, band, pipe); } static int @@ -514,14 +528,10 @@ again: pipeunlock(rpipe); /* - * We want to read more, wake up select/poll. - */ - pipeselwakeup(rpipe, rpipe->pipe_peer, POLL_OUT); - - /* * If the "write-side" is blocked, wake it up now. */ wpipe = rpipe->pipe_peer; + pipeselwakeup(wpipe, POLL_OUT); cv_broadcast(&wpipe->pipe_wcv); if (wakeup_state & PIPE_RESTART) { @@ -558,7 +568,8 @@ unlocked_error: */ if ((bp->size - bp->cnt) >= PIPE_BUF && (ocnt != bp->cnt || (rpipe->pipe_state & PIPE_SIGNALR))) { - pipeselwakeup(rpipe, rpipe->pipe_peer, POLL_OUT); + if ((wpipe = rpipe->pipe_peer) != NULL) + pipeselwakeup(wpipe, POLL_OUT); rpipe->pipe_state &= ~PIPE_SIGNALR; } @@ -695,7 +706,7 @@ pipe_write(file_t *fp, off_t *offset, st * wake up select/poll. */ if (bp->cnt) - pipeselwakeup(rpipe, rpipe, POLL_IN); + pipeselwakeup(rpipe, POLL_IN); if (wakeup_state & PIPE_RESTART) { error = ERESTART; @@ -751,7 +762,7 @@ pipe_write(file_t *fp, off_t *offset, st * We have something to offer, wake up select/poll. */ if (bp->cnt) - pipeselwakeup(rpipe, rpipe, POLL_IN); + pipeselwakeup(rpipe, POLL_IN); /* * Arrange for next read(2) to do a signal. @@ -833,40 +844,57 @@ pipe_poll(file_t *fp, int events) { struct pipe *pipe = fp->f_pipe; struct pipe *ppipe; - int eof = 0; int revents = 0; mutex_enter(pipe->pipe_lock); ppipe = pipe->pipe_peer; - if (events & (POLLIN | POLLRDNORM)) - if ((pipe->pipe_buffer.cnt > 0) || - (pipe->pipe_state & PIPE_EOF)) - revents |= events & (POLLIN | POLLRDNORM); - - eof |= (pipe->pipe_state & PIPE_EOF); + if (fp->f_flag & FREAD) { + struct pipe *rpipe = pipe; - if (ppipe == NULL) - revents |= events & (POLLOUT | POLLWRNORM); - else { - if (events & (POLLOUT | POLLWRNORM)) - if ((ppipe->pipe_state & PIPE_EOF) || ( - (ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt) >= PIPE_BUF)) - revents |= events & (POLLOUT | POLLWRNORM); + /* + * If the writer has been closed, then we can always + * read (possibly returning EOF) without blocking, so + * set POLLIN|POLLRDNORM if requested, and set POLLHUP + * unsolicited to notify reader of the fact. + * + * Otherwise, we can only read without blocking if + * there are bytes in the buffer. + */ + if (rpipe->pipe_state & PIPE_EOF) { + revents |= events & (POLLIN | POLLRDNORM); + revents |= POLLHUP; + } else if (rpipe->pipe_buffer.cnt > 0) { + revents |= events & (POLLIN | POLLRDNORM); + } + } else if (fp->f_flag & FWRITE) { + struct pipe *wpipe = pipe; + struct pipe *rpipe = ppipe; - eof |= (ppipe->pipe_state & PIPE_EOF); + /* + * If the reader has been closed, then any writes will + * immediately fail with EPIPE, so report + * POLLOUT|POLLWRNORM if requested and POLLERR + * unsolicited. + * + * Otherwise, we can only write without blocking if + * there are at least PIPE_BUF bytes free in the + * buffer. + */ + if (rpipe == NULL || (wpipe->pipe_state & PIPE_EOF) != 0) { + revents |= events & (POLLOUT | POLLWRNORM); + revents |= POLLERR; + } else if (rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt >= + PIPE_BUF) { + revents |= events & (POLLOUT | POLLWRNORM); + } + } else { + panic("file %p pipe %p invalid direction flag 0x%x", + fp, pipe, fp->f_flag); } - if (ppipe == NULL || eof) - revents |= POLLHUP; - - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(curlwp, &pipe->pipe_sel); - - if (events & (POLLOUT | POLLWRNORM)) - selrecord(curlwp, &ppipe->pipe_sel); - } + if (revents == 0) + selrecord(curlwp, &pipe->pipe_sel); mutex_exit(pipe->pipe_lock); return (revents); @@ -905,7 +933,7 @@ pipe_close(file_t *fp) struct pipe *pipe = fp->f_pipe; fp->f_pipe = NULL; - pipeclose(pipe); + pipeclose(fp, pipe); return (0); } @@ -974,7 +1002,7 @@ pipe_free_kmem(struct pipe *pipe) * Shutdown the pipe. */ static void -pipeclose(struct pipe *pipe) +pipeclose(struct file *fp, struct pipe *pipe) { kmutex_t *lock; struct pipe *ppipe; @@ -988,7 +1016,6 @@ pipeclose(struct pipe *pipe) KASSERT(lock != NULL); mutex_enter(lock); - pipeselwakeup(pipe, pipe, POLL_HUP); /* * fd_close has issued .fo_restart to wake all waiters on this @@ -1002,14 +1029,35 @@ pipeclose(struct pipe *pipe) KASSERT(!cv_has_waiters(&pipe->pipe_wcv)); /* + * There may, however, be threads waiting in select/poll for + * I/O to be ready on this side of the pipe. Wake them (but + * don't send SIGIO as pipeselwakeup does) so they can fail + * with EBADF/POLLNVAL. + */ + selnotify(&pipe->pipe_sel, 0, NOTE_SUBMIT); + + /* * If the other side is busy, wake it up saying that * we want to close it down, which will prevent peers * from starting new I/O. Once it is no longer busy, * disconnect it. */ + KASSERT(pipe->pipe_peer != NULL || (pipe->pipe_state & PIPE_EOF) != 0); pipe->pipe_state |= PIPE_EOF; if ((ppipe = pipe->pipe_peer) != NULL) { - pipeselwakeup(ppipe, ppipe, POLL_HUP); + if (fp->f_flag & FREAD) { + struct pipe *wpipe = ppipe; + + pipeselwakeup(wpipe, POLL_ERR); + } else if (fp->f_flag & FWRITE) { + struct pipe *rpipe = ppipe; + + pipeselwakeup(rpipe, POLL_HUP); + } else { + panic("file %p pipe %p invalid direction flag 0x%x", + fp, pipe, fp->f_flag); + } + ppipe->pipe_state |= PIPE_EOF; if (ppipe->pipe_busy) { cv_broadcast(&ppipe->pipe_rcv); @@ -1065,24 +1113,6 @@ filt_pipedetach(struct knote *kn) lock = pipe->pipe_lock; mutex_enter(lock); - - switch(kn->kn_filter) { - case EVFILT_WRITE: - /* Need the peer structure, not our own. */ - pipe = pipe->pipe_peer; - - /* If reader end already closed, just return. */ - if (pipe == NULL) { - mutex_exit(lock); - return; - } - - break; - default: - /* Nothing to do. */ - break; - } - KASSERT(kn->kn_hook == pipe); selremove_knote(&pipe->pipe_sel, kn); mutex_exit(lock); @@ -1091,18 +1121,20 @@ filt_pipedetach(struct knote *kn) static int filt_piperead(struct knote *kn, long hint) { - struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *ppipe; + struct pipe *rpipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *wpipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(pipe->pipe_lock); + mutex_enter(rpipe->pipe_lock); + } else { + KASSERT(mutex_owned(rpipe->pipe_lock)); } - ppipe = pipe->pipe_peer; - kn->kn_data = pipe->pipe_buffer.cnt; + wpipe = rpipe->pipe_peer; + kn->kn_data = rpipe->pipe_buffer.cnt; - if ((pipe->pipe_state & PIPE_EOF) || - (ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { + if ((rpipe->pipe_state & PIPE_EOF) || + (wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { knote_set_eof(kn, 0); rv = 1; } else { @@ -1110,7 +1142,9 @@ filt_piperead(struct knote *kn, long hin } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(pipe->pipe_lock); + mutex_exit(rpipe->pipe_lock); + } else { + KASSERT(mutex_owned(rpipe->pipe_lock)); } return rv; } @@ -1118,26 +1152,30 @@ filt_piperead(struct knote *kn, long hin static int filt_pipewrite(struct knote *kn, long hint) { - struct pipe *pipe = ((file_t *)kn->kn_obj)->f_pipe; - struct pipe *ppipe; + struct pipe *wpipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *rpipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(pipe->pipe_lock); + mutex_enter(wpipe->pipe_lock); + } else { + KASSERT(mutex_owned(wpipe->pipe_lock)); } - ppipe = pipe->pipe_peer; + rpipe = wpipe->pipe_peer; - if ((ppipe == NULL) || (ppipe->pipe_state & PIPE_EOF)) { + if ((rpipe == NULL) || (rpipe->pipe_state & PIPE_EOF)) { kn->kn_data = 0; knote_set_eof(kn, 0); rv = 1; } else { - kn->kn_data = ppipe->pipe_buffer.size - ppipe->pipe_buffer.cnt; + kn->kn_data = rpipe->pipe_buffer.size - rpipe->pipe_buffer.cnt; rv = kn->kn_data >= PIPE_BUF; } if ((hint & NOTE_SUBMIT) == 0) { - mutex_exit(pipe->pipe_lock); + mutex_exit(wpipe->pipe_lock); + } else { + KASSERT(mutex_owned(wpipe->pipe_lock)); } return rv; } @@ -1169,16 +1207,18 @@ pipe_kqfilter(file_t *fp, struct knote * switch (kn->kn_filter) { case EVFILT_READ: + if ((fp->f_flag & FREAD) == 0) { + mutex_exit(lock); + return (EINVAL); + } kn->kn_fop = &pipe_rfiltops; break; case EVFILT_WRITE: + if ((fp->f_flag & FWRITE) == 0) { + mutex_exit(lock); + return (EINVAL); + } kn->kn_fop = &pipe_wfiltops; - pipe = pipe->pipe_peer; - if (pipe == NULL) { - /* Other end of pipe has been closed. */ - mutex_exit(lock); - return (EBADF); - } break; default: mutex_exit(lock); diff -r 3ed9f1b9f022 -r ba133cff7ddb tests/lib/libc/sys/t_poll.c --- a/tests/lib/libc/sys/t_poll.c Wed Sep 23 01:44:29 2026 +0000 +++ b/tests/lib/libc/sys/t_poll.c Wed Sep 23 15:34:37 2026 +0000 @@ -1499,14 +1499,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_immediat { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); @@ -1522,14 +1514,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_immediat { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); /* don't fill pipe buf */ check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); @@ -1575,14 +1559,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_delayed_ { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); check_pollclosedpeer_delayed_process(writefd, readfd, @@ -1615,14 +1591,6 @@ ATF_TC_BODY(pollclosedpeer_pipe_delayed_ { int writefd, readfd; - /* - * poll(2) returns POLLHUP|POLLOUT, which is forbidden -- - * POLLHUP and POLLOUT are mutually exclusive. And POLLHUP is - * only supposed to be returned by polling for read, not - * polling for write. So it should be POLLOUT. - */ - atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); - pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); check_pollclosedpeer_delayed_thread(writefd, readfd, # HG changeset patch # User Taylor R Campbell # Date 1790175593 0 # Wed Sep 23 14:59:53 2026 +0000 # Branch trunk # Node ID 0cc06fd711bc37d0c59f6e914eb2d2a09176ac7d # Parent ba133cff7ddb13724e296ad3ccec5f892b19e13e # EXP-Topic riastradh-pr59056-pollhup pipe(2): Don't cv_wait_sig in a loop without breaking on signal. If a signal does arrive, it will turn into a busy wait! Not helpful. But this wait should be limited to scheduling delays for other threads anyway, not for general I/O, so there's no need to be interruptible by a signal. Prompted by: PR kern/59056: poll POLLHUP bugs diff -r ba133cff7ddb -r 0cc06fd711bc sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 15:34:37 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 14:59:53 2026 +0000 @@ -1063,7 +1063,7 @@ pipeclose(struct file *fp, struct pipe * cv_broadcast(&ppipe->pipe_rcv); cv_broadcast(&ppipe->pipe_wcv); while (ppipe->pipe_busy) - cv_wait_sig(&ppipe->pipe_draincv, lock); + cv_wait(&ppipe->pipe_draincv, lock); } ppipe->pipe_peer = NULL; } # HG changeset patch # User Taylor R Campbell # Date 1790181374 0 # Wed Sep 23 16:36:14 2026 +0000 # Branch trunk # Node ID 22fc7c952288686010ffab8030eff592e6828496 # Parent 0cc06fd711bc37d0c59f6e914eb2d2a09176ac7d # EXP-Topic riastradh-pr59056-pollhup pipe(2): Sort includes. No functional change intended. Preparation for: PR kern/58378: Kernel error code origination lacks dtrace probes diff -r 0cc06fd711bc -r 22fc7c952288 sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 14:59:53 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 16:36:14 2026 +0000 @@ -58,26 +58,28 @@ __KERNEL_RCSID(0, "$NetBSD: sys_pipe.c,v 1.168 2025/07/16 19:14:13 kre Exp $"); #include -#include -#include +#include + +#include #include #include #include #include +#include #include -#include -#include +#include +#include #include +#include +#include #include -#include -#include -#include -#include +#include #include #include -#include -#include -#include +#include +#include +#include +#include static int pipe_read(file_t *, off_t *, struct uio *, kauth_cred_t, int); static int pipe_write(file_t *, off_t *, struct uio *, kauth_cred_t, int); # HG changeset patch # User Taylor R Campbell # Date 1790181425 0 # Wed Sep 23 16:37:05 2026 +0000 # Branch trunk # Node ID 8e676fe27c1c6ba6fc1c3910441183369957d462 # Parent 22fc7c952288686010ffab8030eff592e6828496 # EXP-Topic riastradh-pr59056-pollhup pipe(2): Sprinkle SET_ERROR dtrace probes. PR kern/58378: Kernel error code origination lacks dtrace probes diff -r 22fc7c952288 -r 8e676fe27c1c sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Wed Sep 23 16:36:14 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 16:37:05 2026 +0000 @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -216,7 +217,7 @@ pipe1(struct lwp *l, int *fildes, int fl proc_t *p; if (flags & ~(O_CLOEXEC|O_CLOFORK|O_NONBLOCK|O_NOSIGPIPE)) - return EINVAL; + return SET_ERROR(EINVAL); p = curproc; rpipe = wpipe = NULL; getnanotime(&nt); @@ -257,7 +258,7 @@ pipe1(struct lwp *l, int *fildes, int fl fd_affix(p, rf, fildes[0]); fd_affix(p, wf, fildes[1]); - return (0); + return 0; free3: fd_abort(p, rf, fildes[0]); free2: @@ -266,7 +267,7 @@ free2: if (rpipe) pipefree(rpipe); - return (error); + return error; } /* @@ -290,7 +291,7 @@ pipespace(struct pipe *pipe, int size) buffer = (void *)uvm_km_alloc(kernel_map, round_page(size), 0, UVM_KMF_PAGEABLE); if (buffer == NULL) - return (ENOMEM); + return SET_ERROR(ENOMEM); atomic_add_int(&amountpipekva, size); } @@ -301,7 +302,7 @@ pipespace(struct pipe *pipe, int size) pipe->pipe_buffer.in = 0; pipe->pipe_buffer.out = 0; pipe->pipe_buffer.cnt = 0; - return (0); + return 0; } /* @@ -447,10 +448,10 @@ pipe_read(file_t *fp, off_t *offset, str */ if ((fp->f_flag & FNONBLOCK) != 0) { if (__predict_false(uio->uio_resid == 0)) - return (0); + return 0; if (atomic_load_relaxed(&bp->cnt) == 0 && (atomic_load_relaxed(&rpipe->pipe_state) & PIPE_EOF) == 0) - return (EAGAIN); + return SET_ERROR(EAGAIN); } mutex_enter(lock); @@ -518,7 +519,7 @@ again: * Don't block on non-blocking I/O. */ if (fp->f_flag & FNONBLOCK) { - error = EAGAIN; + error = SET_ERROR(EAGAIN); break; } @@ -537,7 +538,7 @@ again: cv_broadcast(&wpipe->pipe_wcv); if (wakeup_state & PIPE_RESTART) { - error = ERESTART; + error = SET_ERROR(ERESTART); goto unlocked_error; } @@ -576,7 +577,7 @@ unlocked_error: } mutex_exit(lock); - return (error); + return error; } static int @@ -602,7 +603,7 @@ pipe_write(file_t *fp, off_t *offset, st */ if (rpipe == NULL || (rpipe->pipe_state & PIPE_EOF) != 0) { mutex_exit(lock); - return EPIPE; + return SET_ERROR(EPIPE); } ++wpipe->pipe_busy; @@ -613,7 +614,7 @@ pipe_write(file_t *fp, off_t *offset, st cv_broadcast(&wpipe->pipe_draincv); } mutex_exit(lock); - return (error); + return error; } bp = &rpipe->pipe_buffer; @@ -699,7 +700,7 @@ pipe_write(file_t *fp, off_t *offset, st * Don't block on non-blocking I/O. */ if (fp->f_flag & FNONBLOCK) { - error = EAGAIN; + error = SET_ERROR(EAGAIN); break; } @@ -711,7 +712,7 @@ pipe_write(file_t *fp, off_t *offset, st pipeselwakeup(rpipe, POLL_IN); if (wakeup_state & PIPE_RESTART) { - error = ERESTART; + error = SET_ERROR(ERESTART); break; } @@ -725,7 +726,7 @@ pipe_write(file_t *fp, off_t *offset, st * XXX Distinguish reader-closed from writer-closed? */ if (rpipe->pipe_state & PIPE_EOF) { - error = EPIPE; + error = SET_ERROR(EPIPE); break; } @@ -773,7 +774,7 @@ pipe_write(file_t *fp, off_t *offset, st pipeunlock(rpipe); mutex_exit(lock); - return (error); + return error; } /* @@ -788,7 +789,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void switch (cmd) { case FIONBIO: - return (0); + return 0; case FIOASYNC: mutex_enter(lock); @@ -798,13 +799,13 @@ pipe_ioctl(file_t *fp, u_long cmd, void pipe->pipe_state &= ~PIPE_ASYNC; } mutex_exit(lock); - return (0); + return 0; case FIONREAD: mutex_enter(lock); *(int *)data = pipe->pipe_buffer.cnt; mutex_exit(lock); - return (0); + return 0; case FIONWRITE: /* Look at other side */ @@ -815,7 +816,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void else *(int *)data = pipe->pipe_buffer.cnt; mutex_exit(lock); - return (0); + return 0; case FIONSPACE: /* Look at other side */ @@ -827,7 +828,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void *(int *)data = pipe->pipe_buffer.size - pipe->pipe_buffer.cnt; mutex_exit(lock); - return (0); + return 0; case TIOCSPGRP: case FIOSETOWN: @@ -838,7 +839,7 @@ pipe_ioctl(file_t *fp, u_long cmd, void return fgetown(pipe->pipe_pgid, cmd, data); } - return (EPASSTHROUGH); + return EPASSTHROUGH; } int @@ -899,7 +900,7 @@ pipe_poll(file_t *fp, int events) selrecord(curlwp, &pipe->pipe_sel); mutex_exit(pipe->pipe_lock); - return (revents); + return revents; } static int @@ -936,7 +937,7 @@ pipe_close(file_t *fp) fp->f_pipe = NULL; pipeclose(fp, pipe); - return (0); + return 0; } static void @@ -970,7 +971,7 @@ pipe_fpathconf(struct file *fp, int name *retval = PIPE_BUF; return 0; default: - return EINVAL; + return SET_ERROR(EINVAL); } } @@ -978,7 +979,7 @@ static int pipe_posix_fadvise(struct file *fp, off_t offset, off_t len, int advice) { - return ESPIPE; + return SET_ERROR(ESPIPE); } static void @@ -1211,27 +1212,27 @@ pipe_kqfilter(file_t *fp, struct knote * case EVFILT_READ: if ((fp->f_flag & FREAD) == 0) { mutex_exit(lock); - return (EINVAL); + return SET_ERROR(EINVAL); } kn->kn_fop = &pipe_rfiltops; break; case EVFILT_WRITE: if ((fp->f_flag & FWRITE) == 0) { mutex_exit(lock); - return (EINVAL); + return SET_ERROR(EINVAL); } kn->kn_fop = &pipe_wfiltops; break; default: mutex_exit(lock); - return (EINVAL); + return SET_ERROR(EINVAL); } kn->kn_hook = pipe; selrecord_knote(&pipe->pipe_sel, kn); mutex_exit(lock); - return (0); + return 0; } /* # HG changeset patch # User Taylor R Campbell # Date 1790172792 0 # Wed Sep 23 14:13:12 2026 +0000 # Branch trunk # Node ID def4e5373fd42d8d7bd8c4fd71bc46ebf9f54ef5 # Parent 8e676fe27c1c6ba6fc1c3910441183369957d462 # EXP-Topic riastradh-pr59056-pollhup t_fdrestart: Test restarting poll and select on close too. They must promptly report POLLNVAL/EBADF when a file descriptor they are listening for is closed, just like read and write. Note: kevent doesn't get restarted when an fd registered with it is closed; the registration is simply silently discarded instead. Hence no corresponding kevent tests here. PR kern/57659: closing pipe writefd fails to wake concurrent write on same writefd diff -r 8e676fe27c1c -r def4e5373fd4 tests/kernel/t_fdrestart.c --- a/tests/kernel/t_fdrestart.c Wed Sep 23 16:37:05 2026 +0000 +++ b/tests/kernel/t_fdrestart.c Wed Sep 23 14:13:12 2026 +0000 @@ -32,11 +32,13 @@ __RCSID("$NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $"); #include +#include #include #include #include #include +#include #include #include @@ -86,7 +88,7 @@ doread(struct fdrestart *F) nread = rump_sys_read(F->fd, &c, sizeof(c)); ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread); error = errno; - ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error, + ATF_CHECK_EQ_MSG(error, ERESTART, "errno=%d (%s)", error, strerror(error)); /* @@ -96,11 +98,105 @@ doread(struct fdrestart *F) nread = rump_sys_read(F->fd, &c, sizeof(c)); ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread); error = errno; - ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error, + ATF_CHECK_EQ_MSG(error, EBADF, "errno=%d (%s)", error, strerror(error)); } static void +dopollread(struct fdrestart *F) +{ + struct pollfd pfd; + int nfds; + + /* + * Prepare poll inputs so we can start ASAP when we cross the + * barrier. + */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = F->fd; + pfd.events = POLLIN; + + /* + * Wait for the other thread to be ready. + */ + waitforbarrier(F, "reader"); + + /* + * Wait for readability. This should block, and then, when the + * other thread closes the fd, should be woken to fail with + * ERESTART. + */ + nfds = rump_sys_poll(&pfd, 1, -1); + ATF_REQUIRE_EQ_MSG(nfds, 1, "nfds=%d", nfds); + ATF_CHECK_EQ_MSG(pfd.revents, POLLNVAL, "revents=0x%x", pfd.revents); + + /* + * Further attempts to poll should return POLLNVAL immediately + * because the fd has been closed. + */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = F->fd; + pfd.events = POLLOUT; + nfds = rump_sys_poll(&pfd, 1, -1); + ATF_REQUIRE_EQ_MSG(nfds, 1, "nfds=%d", nfds); + ATF_CHECK_EQ_MSG(pfd.revents, POLLNVAL, "revents=0x%x", pfd.revents); +} + +static void +doselectread(struct fdrestart *F) +{ + fd_set rfd; + int nfds; + + /* + * Prepare select inputs so we can start ASAP when we cross the + * barrier. + */ + FD_ZERO(&rfd); + FD_SET(F->fd, &rfd); + + /* + * Wait for the other thread to be ready. + */ + waitforbarrier(F, "reader"); + + /* + * Wait for readability. This should block, and then, when the + * other thread closes the fd, should be woken to fail with + * ERESTART. + */ + nfds = rump_sys_select(F->fd + 1, &rfd, /*wfd*/NULL, /*efd*/NULL, + /*timeout*/NULL); + ATF_CHECK_ERRNO(EBADF, nfds == -1); + + /* + * Further attempts to select should fail with EBADF + * immediately because the fd has been closed. + */ + FD_ZERO(&rfd); + FD_SET(F->fd, &rfd); + nfds = rump_sys_select(F->fd + 1, &rfd, /*wfd*/NULL, /*efd*/NULL, + /*timeout*/NULL); + ATF_CHECK_ERRNO(EBADF, nfds == -1); +} + +static void +fillpipebuf(int fd) +{ + static const char buf[1024*1024]; /* XXX >BIG_PIPE_SIZE */ + + for (;;) { + int nspace; + + RL(rump_sys_ioctl(fd, FIONSPACE, &nspace)); + ATF_REQUIRE_MSG(nspace >= 0, "nspace=%d", nspace); + if (nspace == 0) + break; + RL(rump_sys_write(fd, buf, (size_t)nspace)); + } +} + +static void dowrite(struct fdrestart *F) { static const char buf[1024*1024]; /* XXX >BIG_PIPE_SIZE */ @@ -110,15 +206,7 @@ dowrite(struct fdrestart *F) /* * Make sure the pipe's buffer is full first. */ - for (;;) { - int nspace; - - RL(rump_sys_ioctl(F->fd, FIONSPACE, &nspace)); - ATF_REQUIRE_MSG(nspace >= 0, "nspace=%d", nspace); - if (nspace == 0) - break; - RL(rump_sys_write(F->fd, buf, (size_t)nspace)); - } + fillpipebuf(F->fd); /* * Wait for the other thread to be ready. @@ -132,7 +220,7 @@ dowrite(struct fdrestart *F) nwrit = rump_sys_write(F->fd, buf, sizeof(buf)); ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit); error = errno; - ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error, + ATF_CHECK_EQ_MSG(error, ERESTART, "errno=%d (%s)", error, strerror(error)); /* @@ -142,10 +230,98 @@ dowrite(struct fdrestart *F) nwrit = rump_sys_write(F->fd, buf, sizeof(buf)); ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit); error = errno; - ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error, + ATF_CHECK_EQ_MSG(error, EBADF, "errno=%d (%s)", error, strerror(error)); } +static void +dopollwrite(struct fdrestart *F) +{ + struct pollfd pfd; + int nfds; + + /* + * Make sure the pipe's buffer is full first. + */ + fillpipebuf(F->fd); + + /* + * Prepare poll inputs so we can start ASAP when we cross the + * barrier. + */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = F->fd; + pfd.events = POLLOUT; + + /* + * Wait for the other thread to be ready. + */ + waitforbarrier(F, "writer"); + + /* + * Wait for writability. This should block, and then, when the + * other thread closes the fd, should be woken to fail with + * ERESTART. + */ + nfds = rump_sys_poll(&pfd, 1, -1); + ATF_REQUIRE_EQ_MSG(nfds, 1, "nfds=%d", nfds); + ATF_CHECK_EQ_MSG(pfd.revents, POLLNVAL, "revents=0x%x", pfd.revents); + + /* + * Further attempts to poll should return POLLNVAL immediately + * because the fd has been closed. + */ + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = F->fd; + pfd.events = POLLOUT; + nfds = rump_sys_poll(&pfd, 1, -1); + ATF_REQUIRE_EQ_MSG(nfds, 1, "nfds=%d", nfds); + ATF_CHECK_EQ_MSG(pfd.revents, POLLNVAL, "revents=0x%x", pfd.revents); +} + +static void +doselectwrite(struct fdrestart *F) +{ + fd_set wfd; + int nfds; + + /* + * Make sure the pipe's buffer is full first. + */ + fillpipebuf(F->fd); + + /* + * Prepare select inputs so we can start ASAP when we cross the + * barrier. + */ + FD_ZERO(&wfd); + FD_SET(F->fd, &wfd); + + /* + * Wait for the other thread to be ready. + */ + waitforbarrier(F, "writer"); + + /* + * Wait for writability. This should block, and then, when the + * other thread closes the fd, should be woken to fail with + * ERESTART. + */ + nfds = rump_sys_select(F->fd + 1, /*rfd*/NULL, &wfd, /*efd*/NULL, + /*timeout*/NULL); + ATF_CHECK_ERRNO(EBADF, nfds == -1); + + /* + * Further attempts to select should fail with EBADF + * immediately because the fd has been closed. + */ + FD_ZERO(&wfd); + FD_SET(F->fd, &wfd); + nfds = rump_sys_select(F->fd + 1, /*rfd*/NULL, &wfd, /*efd*/NULL, + /*timeout*/NULL); + ATF_CHECK_ERRNO(EBADF, nfds == -1); +} + static void * doit(void *cookie) { @@ -168,8 +344,7 @@ testfdrestart(struct fdrestart *F) { pthread_t t; - ATF_REQUIRE_MSG(signal(SIGALRM, &on_sigalrm) != SIG_ERR, - "errno=%d (%s)", errno, strerror(errno)); + REQUIRE_LIBC(signal(SIGALRM, &on_sigalrm), SIG_ERR); RZ(pthread_barrier_init(&F->barrier, NULL, 2)); RZ(pthread_create(&t, NULL, &doit, F)); @@ -201,6 +376,48 @@ ATF_TC_BODY(pipe_read, tc) testfdrestart(F); } +ATF_TC(pipe_pollread); +ATF_TC_HEAD(pipe_pollread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for pipe readability wakes on close"); +} +ATF_TC_BODY(pipe_pollread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_pipe(fd)); + + memset(F, 0, sizeof(*F)); + F->op = &dopollread; + F->fd = fd[0]; + testfdrestart(F); +} + +ATF_TC(pipe_selectread); +ATF_TC_HEAD(pipe_selectread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for pipe readability wakes on close"); +} +ATF_TC_BODY(pipe_selectread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_pipe(fd)); + + memset(F, 0, sizeof(*F)); + F->op = &doselectread; + F->fd = fd[0]; + testfdrestart(F); +} + ATF_TC(pipe_write); ATF_TC_HEAD(pipe_write, tc) { @@ -221,6 +438,48 @@ ATF_TC_BODY(pipe_write, tc) testfdrestart(F); } +ATF_TC(pipe_pollwrite); +ATF_TC_HEAD(pipe_pollwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for pipe writability wakes on close"); +} +ATF_TC_BODY(pipe_pollwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_pipe(fd)); + + memset(F, 0, sizeof(*F)); + F->op = &dopollwrite; + F->fd = fd[1]; + testfdrestart(F); +} + +ATF_TC(pipe_selectwrite); +ATF_TC_HEAD(pipe_selectwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for pipe writability wakes on close"); +} +ATF_TC_BODY(pipe_selectwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_pipe(fd)); + + memset(F, 0, sizeof(*F)); + F->op = &doselectwrite; + F->fd = fd[1]; + testfdrestart(F); +} + ATF_TC(socketpair_read); ATF_TC_HEAD(socketpair_read, tc) { @@ -241,6 +500,48 @@ ATF_TC_BODY(socketpair_read, tc) testfdrestart(F); } +ATF_TC(socketpair_pollread); +ATF_TC_HEAD(socketpair_pollread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test socketpair poll for readability wakes on close"); +} +ATF_TC_BODY(socketpair_pollread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd)); + + memset(F, 0, sizeof(*F)); + F->op = &dopollread; + F->fd = fd[0]; + testfdrestart(F); +} + +ATF_TC(socketpair_selectread); +ATF_TC_HEAD(socketpair_selectread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test socketpair select for readability wakes on close"); +} +ATF_TC_BODY(socketpair_selectread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd)); + + memset(F, 0, sizeof(*F)); + F->op = &doselectread; + F->fd = fd[0]; + testfdrestart(F); +} + ATF_TC(socketpair_write); ATF_TC_HEAD(socketpair_write, tc) { @@ -261,12 +562,62 @@ ATF_TC_BODY(socketpair_write, tc) testfdrestart(F); } +ATF_TC(socketpair_pollwrite); +ATF_TC_HEAD(socketpair_pollwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for socket writability wakes on close"); +} +ATF_TC_BODY(socketpair_pollwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd)); + + memset(F, 0, sizeof(*F)); + F->op = &dopollwrite; + F->fd = fd[0]; + testfdrestart(F); +} + +ATF_TC(socketpair_selectwrite); +ATF_TC_HEAD(socketpair_selectwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for socket writability wakes on close"); +} +ATF_TC_BODY(socketpair_selectwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int fd[2]; + + rump_init(); + + RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd)); + + memset(F, 0, sizeof(*F)); + F->op = &doselectwrite; + F->fd = fd[0]; + testfdrestart(F); +} + ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, pipe_pollread); + ATF_TP_ADD_TC(tp, pipe_pollwrite); ATF_TP_ADD_TC(tp, pipe_read); + ATF_TP_ADD_TC(tp, pipe_selectread); + ATF_TP_ADD_TC(tp, pipe_selectwrite); ATF_TP_ADD_TC(tp, pipe_write); + ATF_TP_ADD_TC(tp, socketpair_pollread); + ATF_TP_ADD_TC(tp, socketpair_pollwrite); ATF_TP_ADD_TC(tp, socketpair_read); + ATF_TP_ADD_TC(tp, socketpair_selectread); + ATF_TP_ADD_TC(tp, socketpair_selectwrite); ATF_TP_ADD_TC(tp, socketpair_write); return atf_no_error(); # HG changeset patch # User Taylor R Campbell # Date 1790175378 0 # Wed Sep 23 14:56:18 2026 +0000 # Branch trunk # Node ID 76435b70b9c7290964237b5fb8c6265d877a74f9 # Parent def4e5373fd42d8d7bd8c4fd71bc46ebf9f54ef5 # EXP-Topic riastradh-pr59056-pollhup t_fdrestart: Test named fifos too. Turns out we're missing a wakeup on close for read and write on fifos too! PR kern/57659: closing pipe writefd fails to wake concurrent write on same writefd diff -r def4e5373fd4 -r 76435b70b9c7 tests/kernel/Makefile --- a/tests/kernel/Makefile Wed Sep 23 14:13:12 2026 +0000 +++ b/tests/kernel/Makefile Wed Sep 23 14:56:18 2026 +0000 @@ -73,6 +73,9 @@ SCRIPTSDIR= ${TESTSDIR} SCRIPTS= h_interpreter.sh LDADD.t_fdrestart+= -lpthread +LDADD.t_fdrestart+= -lrumpvfs +LDADD.t_fdrestart+= -lrumpvfs_fifofs +LDADD.t_fdrestart+= -lrumpfs_tmpfs LDADD.t_fdrestart+= -lrumpnet_local LDADD.t_fdrestart+= -lrumpnet_net LDADD.t_fdrestart+= -lrumpnet_netinet diff -r def4e5373fd4 -r 76435b70b9c7 tests/kernel/t_fdrestart.c --- a/tests/kernel/t_fdrestart.c Wed Sep 23 14:13:12 2026 +0000 +++ b/tests/kernel/t_fdrestart.c Wed Sep 23 14:56:18 2026 +0000 @@ -32,12 +32,16 @@ __RCSID("$NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $"); #include +#include #include #include #include +#include + #include #include +#include #include #include #include @@ -356,6 +360,176 @@ testfdrestart(struct fdrestart *F) (void)alarm(0); /* clear the deadline */ } +static int +fifo_setup(int flags) +{ + struct tmpfs_args args; + int rfd, wfd, fd; + + /* + * Mount a tmpfs so we can use fifos. The rumpfs shim doesn't + * support them, or at least doesn't support setting and + * clearing O_NONBLOCK with fcntl on them. + */ + memset(&args, 0, sizeof(args)); + args.ta_version = TMPFS_ARGS_VERSION; + args.ta_root_mode = 0777; + RL(rump_sys_mkdir("/mnt", 0777)); + RL(rump_sys_mount(MOUNT_TMPFS, "/mnt", 0, &args, sizeof(args))); + + /* + * Create a fifo. + */ + RL(rump_sys_mkfifo("/mnt/fifo", 0600)); + + /* + * Open the reader side first. This is necessary because it is + * allowed to succeed without blocking when there is no peer, + * whereas opening the writer side either blocks or fails with + * ENXIO when there is no peer. + */ + RL(rfd = rump_sys_open("/mnt/fifo", O_RDONLY|O_NONBLOCK)); + + /* + * If the caller asked for the read side, return it. + * Otherwise, open the write side (but leave the reader side + * open so that write will block rather than fail with + * EPIPE/SIGPIPE). + */ + switch (flags) { + case O_RDONLY: + fd = rfd; + goto out; + case O_WRONLY: + RL(wfd = rump_sys_open("/mnt/fifo", O_WRONLY|O_NONBLOCK)); + fd = wfd; + goto out; + default: + atf_tc_fail("invalid fifo setup flags"); + } + +out: /* + * Whichever side the caller wanted, make it blocking. + */ + RL(flags = rump_sys_fcntl(fd, F_GETFL)); + RL(rump_sys_fcntl(fd, F_SETFL, flags & ~O_NONBLOCK)); + return fd; +} + +ATF_TC(fifo_read); +ATF_TC_HEAD(fifo_read, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test named fifo read fails on close"); +} +ATF_TC_BODY(fifo_read, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + + rump_init(); + + memset(F, 0, sizeof(*F)); + F->op = &doread; + F->fd = fifo_setup(O_RDONLY); + atf_tc_expect_fail("PR kern/57659:" /* similar bug for fifos */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + +ATF_TC(fifo_pollread); +ATF_TC_HEAD(fifo_pollread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for named fifo readability fails on close"); +} +ATF_TC_BODY(fifo_pollread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + + rump_init(); + + memset(F, 0, sizeof(*F)); + F->op = &dopollread; + F->fd = fifo_setup(O_RDONLY); + testfdrestart(F); +} + +ATF_TC(fifo_selectread); +ATF_TC_HEAD(fifo_selectread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for named fifo readability fails on close"); +} +ATF_TC_BODY(fifo_selectread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + + rump_init(); + + memset(F, 0, sizeof(*F)); + F->op = &doselectread; + F->fd = fifo_setup(O_RDONLY); + testfdrestart(F); +} + +ATF_TC(fifo_write); +ATF_TC_HEAD(fifo_write, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test named fifo write fails on close"); +} +ATF_TC_BODY(fifo_write, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + + rump_init(); + + memset(F, 0, sizeof(*F)); + F->op = &dowrite; + F->fd = fifo_setup(O_WRONLY); + atf_tc_expect_fail("PR kern/57659:" /* similar bug for fifos */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + +ATF_TC(fifo_pollwrite); +ATF_TC_HEAD(fifo_pollwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for named fifo writability fails on close"); +} +ATF_TC_BODY(fifo_pollwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + + rump_init(); + + memset(F, 0, sizeof(*F)); + F->op = &dopollwrite; + F->fd = fifo_setup(O_WRONLY); + testfdrestart(F); +} + +ATF_TC(fifo_selectwrite); +ATF_TC_HEAD(fifo_selectwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for named fifo writability fails on close"); +} +ATF_TC_BODY(fifo_selectwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + + rump_init(); + + memset(F, 0, sizeof(*F)); + F->op = &doselectwrite; + F->fd = fifo_setup(O_WRONLY); + testfdrestart(F); +} + ATF_TC(pipe_read); ATF_TC_HEAD(pipe_read, tc) { @@ -607,6 +781,12 @@ ATF_TC_BODY(socketpair_selectwrite, tc) ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, fifo_pollread); + ATF_TP_ADD_TC(tp, fifo_pollwrite); + ATF_TP_ADD_TC(tp, fifo_read); + ATF_TP_ADD_TC(tp, fifo_selectread); + ATF_TP_ADD_TC(tp, fifo_selectwrite); + ATF_TP_ADD_TC(tp, fifo_write); ATF_TP_ADD_TC(tp, pipe_pollread); ATF_TP_ADD_TC(tp, pipe_pollwrite); ATF_TP_ADD_TC(tp, pipe_read); # HG changeset patch # User Taylor R Campbell # Date 1790180158 0 # Wed Sep 23 16:15:58 2026 +0000 # Branch trunk # Node ID 61050fa7939e37731a5ebdddc0e0b2f1a41ecebe # Parent 76435b70b9c7290964237b5fb8c6265d877a74f9 # EXP-Topic riastradh-pr59056-pollhup t_fdrestart: Test ptys too. More bugs of this class! PR kern/57659: closing pipe writefd fails to wake concurrent write on same writefd diff -r 76435b70b9c7 -r 61050fa7939e tests/kernel/Makefile --- a/tests/kernel/Makefile Wed Sep 23 14:56:18 2026 +0000 +++ b/tests/kernel/Makefile Wed Sep 23 16:15:58 2026 +0000 @@ -73,8 +73,10 @@ SCRIPTSDIR= ${TESTSDIR} SCRIPTS= h_interpreter.sh LDADD.t_fdrestart+= -lpthread +LDADD.t_fdrestart+= -lrumpkern_tty LDADD.t_fdrestart+= -lrumpvfs LDADD.t_fdrestart+= -lrumpvfs_fifofs +LDADD.t_fdrestart+= -lrumpfs_ptyfs LDADD.t_fdrestart+= -lrumpfs_tmpfs LDADD.t_fdrestart+= -lrumpnet_local LDADD.t_fdrestart+= -lrumpnet_net diff -r 76435b70b9c7 -r 61050fa7939e tests/kernel/t_fdrestart.c --- a/tests/kernel/t_fdrestart.c Wed Sep 23 14:56:18 2026 +0000 +++ b/tests/kernel/t_fdrestart.c Wed Sep 23 16:15:58 2026 +0000 @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include #include #include @@ -416,6 +418,36 @@ out: /* return fd; } +static void +ptysetup(int *hostfd, int *appfd) +{ + struct ptyfs_args args; + struct ptmget pm; + struct termios t; + char *pts; + + (void)rump_sys_mkdir("/dev", 0777); + (void)rump_sys_mkdir("/dev/pts", 0777); + + memset(&args, 0, sizeof(args)); + args.version = PTYFS_ARGSVERSION; + args.mode = 0777; + RL(rump_sys_mount(MOUNT_PTYFS, "/dev/pts", 0, &args, sizeof(args))); + + RL(*hostfd = rump_sys_open("/dev/ptmx", O_RDWR|O_NOCTTY)); + RL(rump_sys_ioctl(*hostfd, TIOCGRANTPT, 0)); + /* unlockpt -- noop */ + RL(rump_sys_ioctl(*hostfd, TIOCPTSNAME, &pm)); + pts = pm.sn; + RL(*appfd = rump_sys_open(pts, O_RDWR|O_NOCTTY)); + + RL(rump_sys_ioctl(*appfd, TIOCGETA, &t)); + t.c_lflag &= ~ICANON; /* block rather than drop input */ + RL(rump_sys_ioctl(*appfd, TIOCSETA, &t)); + + fprintf(stderr, "hostfd=%d appfd=%d\n", *hostfd, *appfd); +} + ATF_TC(fifo_read); ATF_TC_HEAD(fifo_read, tc) { @@ -654,6 +686,272 @@ ATF_TC_BODY(pipe_selectwrite, tc) testfdrestart(F); } +ATF_TC(ptyhost_read); +ATF_TC_HEAD(ptyhost_read, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test ptyhost read fails on close"); +} +ATF_TC_BODY(ptyhost_read, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &doread; + F->fd = hostfd; + atf_tc_expect_fail("PR kern/57659:" /* similar bug for pty host side */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + +ATF_TC(ptyhost_pollread); +ATF_TC_HEAD(ptyhost_pollread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for ptyhost readability wakes on close"); +} +ATF_TC_BODY(ptyhost_pollread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &dopollread; + F->fd = hostfd; + testfdrestart(F); +} + +ATF_TC(ptyhost_selectread); +ATF_TC_HEAD(ptyhost_selectread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for ptyhost readability wakes on close"); +} +ATF_TC_BODY(ptyhost_selectread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &doselectread; + F->fd = hostfd; + testfdrestart(F); +} + +ATF_TC(ptyhost_write); +ATF_TC_HEAD(ptyhost_write, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test ptyhost write fails on close"); +} +ATF_TC_BODY(ptyhost_write, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &dowrite; + F->fd = hostfd; + atf_tc_expect_fail("PR kern/57659:" /* similar bug for pty host side */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + +ATF_TC(ptyhost_pollwrite); +ATF_TC_HEAD(ptyhost_pollwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for ptyhost writability wakes on close"); +} +ATF_TC_BODY(ptyhost_pollwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &dopollwrite; + F->fd = hostfd; + testfdrestart(F); +} + +ATF_TC(ptyhost_selectwrite); +ATF_TC_HEAD(ptyhost_selectwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for ptyhost writability wakes on close"); +} +ATF_TC_BODY(ptyhost_selectwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &doselectwrite; + F->fd = hostfd; + testfdrestart(F); +} + +ATF_TC(ptyapp_read); +ATF_TC_HEAD(ptyapp_read, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test ptyapp read fails on close"); +} +ATF_TC_BODY(ptyapp_read, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &doread; + F->fd = appfd; + atf_tc_expect_fail("PR kern/57659:" /* similar bug for pty app side */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + +ATF_TC(ptyapp_pollread); +ATF_TC_HEAD(ptyapp_pollread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for ptyapp readability wakes on close"); +} +ATF_TC_BODY(ptyapp_pollread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &dopollread; + F->fd = appfd; + testfdrestart(F); +} + +ATF_TC(ptyapp_selectread); +ATF_TC_HEAD(ptyapp_selectread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for ptyapp readability wakes on close"); +} +ATF_TC_BODY(ptyapp_selectread, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &doselectread; + F->fd = appfd; + testfdrestart(F); +} + +ATF_TC(ptyapp_write); +ATF_TC_HEAD(ptyapp_write, tc) +{ + atf_tc_set_md_var(tc, "descr", "Test ptyapp write fails on close"); +} +ATF_TC_BODY(ptyapp_write, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &dowrite; + F->fd = appfd; + atf_tc_expect_fail("PR kern/57659:" /* similar bug for pty app side */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + +ATF_TC(ptyapp_pollwrite); +ATF_TC_HEAD(ptyapp_pollwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test poll waiting for ptyapp writability wakes on close"); +} +ATF_TC_BODY(ptyapp_pollwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &dopollwrite; + F->fd = appfd; + atf_tc_expect_fail("PR kern/57659:" /* similar bug for pty app side */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + +ATF_TC(ptyapp_selectwrite); +ATF_TC_HEAD(ptyapp_selectwrite, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test select waiting for ptyapp writability wakes on close"); +} +ATF_TC_BODY(ptyapp_selectwrite, tc) +{ + struct fdrestart fdrestart, *F = &fdrestart; + int hostfd, appfd; + + rump_init(); + + ptysetup(&hostfd, &appfd); + + memset(F, 0, sizeof(*F)); + F->op = &doselectwrite; + F->fd = appfd; + atf_tc_expect_fail("PR kern/57659:" /* similar bug for pty app side */ + " closing pipe writefd fails to wake concurrent write" + " on same writefd"); + testfdrestart(F); +} + ATF_TC(socketpair_read); ATF_TC_HEAD(socketpair_read, tc) { @@ -793,6 +1091,18 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, pipe_selectread); ATF_TP_ADD_TC(tp, pipe_selectwrite); ATF_TP_ADD_TC(tp, pipe_write); + ATF_TP_ADD_TC(tp, ptyapp_pollread); + ATF_TP_ADD_TC(tp, ptyapp_pollwrite); + ATF_TP_ADD_TC(tp, ptyapp_read); + ATF_TP_ADD_TC(tp, ptyapp_selectread); + ATF_TP_ADD_TC(tp, ptyapp_selectwrite); + ATF_TP_ADD_TC(tp, ptyapp_write); + ATF_TP_ADD_TC(tp, ptyhost_pollread); + ATF_TP_ADD_TC(tp, ptyhost_pollwrite); + ATF_TP_ADD_TC(tp, ptyhost_read); + ATF_TP_ADD_TC(tp, ptyhost_selectread); + ATF_TP_ADD_TC(tp, ptyhost_selectwrite); + ATF_TP_ADD_TC(tp, ptyhost_write); ATF_TP_ADD_TC(tp, socketpair_pollread); ATF_TP_ADD_TC(tp, socketpair_pollwrite); ATF_TP_ADD_TC(tp, socketpair_read);