diff -r c96fe1ab6a49 -r 61050fa7939e sys/kern/sys_pipe.c --- a/sys/kern/sys_pipe.c Sun Sep 20 03:58:19 2026 +0000 +++ b/sys/kern/sys_pipe.c Wed Sep 23 16:15:58 2026 +0000 @@ -58,26 +58,29 @@ __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 +#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); @@ -126,12 +129,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 *); @@ -213,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); @@ -254,14 +258,16 @@ 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: - pipeclose(wpipe); - pipeclose(rpipe); + if (wpipe) + pipefree(wpipe); + if (rpipe) + pipefree(rpipe); - return (error); + return error; } /* @@ -285,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); } @@ -296,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; } /* @@ -366,14 +372,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; @@ -395,12 +415,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 @@ -408,6 +428,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; @@ -427,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); @@ -487,6 +508,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; @@ -495,7 +519,7 @@ again: * Don't block on non-blocking I/O. */ if (fp->f_flag & FNONBLOCK) { - error = EAGAIN; + error = SET_ERROR(EAGAIN); break; } @@ -506,20 +530,15 @@ again: */ pipeunlock(rpipe); -#if 1 /* XXX (dsl) I'm sure these aren't needed here ... */ - /* - * 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. */ - cv_broadcast(&rpipe->pipe_wcv); -#endif + wpipe = rpipe->pipe_peer; + pipeselwakeup(wpipe, POLL_OUT); + cv_broadcast(&wpipe->pipe_wcv); if (wakeup_state & PIPE_RESTART) { - error = ERESTART; + error = SET_ERROR(ERESTART); goto unlocked_error; } @@ -538,11 +557,11 @@ 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) { - cv_broadcast(&rpipe->pipe_wcv); + if ((wpipe = rpipe->pipe_peer) != NULL) + cv_broadcast(&wpipe->pipe_wcv); } /* @@ -552,12 +571,13 @@ 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; } mutex_exit(lock); - return (error); + return error; } static int @@ -571,34 +591,33 @@ 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; + return SET_ERROR(EPIPE); } ++wpipe->pipe_busy; /* Acquire the long-term pipe lock */ - if ((error = pipelock(wpipe, true)) != 0) { + 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); - return (error); + return error; } - bp = &wpipe->pipe_buffer; + bp = &rpipe->pipe_buffer; /* * If it is advantageous to resize the pipe buffer, do so. @@ -607,7 +626,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,13 +694,13 @@ 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. */ if (fp->f_flag & FNONBLOCK) { - error = EAGAIN; + error = SET_ERROR(EAGAIN); break; } @@ -690,25 +709,30 @@ pipe_write(file_t *fp, off_t *offset, st * wake up select/poll. */ if (bp->cnt) - pipeselwakeup(wpipe, wpipe, POLL_IN); + pipeselwakeup(rpipe, POLL_IN); if (wakeup_state & PIPE_RESTART) { - error = ERESTART; + error = SET_ERROR(ERESTART); break; } /* * 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 (wpipe->pipe_state & PIPE_EOF) { - error = EPIPE; + if (rpipe->pipe_state & PIPE_EOF) { + error = SET_ERROR(EPIPE); break; } - pipeunlock(wpipe); + pipeunlock(rpipe); error = cv_wait_sig(&wpipe->pipe_wcv, lock); - (void)pipelock(wpipe, false); + (void)pipelock(rpipe, false); if (error != 0) break; wakeup_state = wpipe->pipe_state; @@ -717,36 +741,40 @@ 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) { - cv_broadcast(&wpipe->pipe_rcv); + cv_broadcast(&rpipe->pipe_rcv); } /* * 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; 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, 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); + return error; } /* @@ -761,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); @@ -771,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 */ @@ -788,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 */ @@ -800,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: @@ -811,51 +839,68 @@ pipe_ioctl(file_t *fp, u_long cmd, void return fgetown(pipe->pipe_pgid, cmd, data); } - return (EPASSTHROUGH); + return EPASSTHROUGH; } int pipe_poll(file_t *fp, int events) { - struct pipe *rpipe = fp->f_pipe; - struct pipe *wpipe; - int eof = 0; + struct pipe *pipe = fp->f_pipe; + struct pipe *ppipe; 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)) - revents |= events & (POLLIN | POLLRDNORM); - - eof |= (rpipe->pipe_state & PIPE_EOF); + if (fp->f_flag & FREAD) { + struct pipe *rpipe = pipe; - if (wpipe == 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)) - 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 |= (wpipe->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 (wpipe == NULL || eof) - revents |= POLLHUP; - - if (revents == 0) { - if (events & (POLLIN | POLLRDNORM)) - selrecord(curlwp, &rpipe->pipe_sel); + if (revents == 0) + selrecord(curlwp, &pipe->pipe_sel); + mutex_exit(pipe->pipe_lock); - if (events & (POLLOUT | POLLWRNORM)) - selrecord(curlwp, &wpipe->pipe_sel); - } - mutex_exit(rpipe->pipe_lock); - - return (revents); + return revents; } static int @@ -891,8 +936,8 @@ pipe_close(file_t *fp) struct pipe *pipe = fp->f_pipe; fp->f_pipe = NULL; - pipeclose(pipe); - return (0); + pipeclose(fp, pipe); + return 0; } static void @@ -907,8 +952,11 @@ pipe_restart(file_t *fp) */ 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! */ + /* + * 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); @@ -923,7 +971,7 @@ pipe_fpathconf(struct file *fp, int name *retval = PIPE_BUF; return 0; default: - return EINVAL; + return SET_ERROR(EINVAL); } } @@ -931,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 @@ -957,46 +1005,69 @@ 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; - 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); + + /* + * 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)); + + /* + * 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 blocked, wake it up saying that - * we want to close it down. + * 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 (pipe->pipe_busy) { - while (pipe->pipe_busy) { - cv_broadcast(&pipe->pipe_wcv); - cv_wait_sig(&pipe->pipe_draincv, lock); + if ((ppipe = pipe->pipe_peer) != NULL) { + 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); } - } - /* - * 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(&ppipe->pipe_draincv, lock); + } ppipe->pipe_peer = NULL; } @@ -1016,7 +1087,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; @@ -1039,24 +1116,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); @@ -1071,6 +1130,8 @@ filt_piperead(struct knote *kn, long hin if ((hint & NOTE_SUBMIT) == 0) { mutex_enter(rpipe->pipe_lock); + } else { + KASSERT(mutex_owned(rpipe->pipe_lock)); } wpipe = rpipe->pipe_peer; kn->kn_data = rpipe->pipe_buffer.cnt; @@ -1085,6 +1146,8 @@ filt_piperead(struct knote *kn, long hin if ((hint & NOTE_SUBMIT) == 0) { mutex_exit(rpipe->pipe_lock); + } else { + KASSERT(mutex_owned(rpipe->pipe_lock)); } return rv; } @@ -1092,26 +1155,30 @@ 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 *wpipe = ((file_t *)kn->kn_obj)->f_pipe; + struct pipe *rpipe; int rv; if ((hint & NOTE_SUBMIT) == 0) { - mutex_enter(rpipe->pipe_lock); + mutex_enter(wpipe->pipe_lock); + } else { + KASSERT(mutex_owned(wpipe->pipe_lock)); } - wpipe = rpipe->pipe_peer; + rpipe = wpipe->pipe_peer; - if ((wpipe == NULL) || (wpipe->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 = wpipe->pipe_buffer.size - wpipe->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(rpipe->pipe_lock); + mutex_exit(wpipe->pipe_lock); + } else { + KASSERT(mutex_owned(wpipe->pipe_lock)); } return rv; } @@ -1143,27 +1210,29 @@ pipe_kqfilter(file_t *fp, struct knote * switch (kn->kn_filter) { case EVFILT_READ: + if ((fp->f_flag & FREAD) == 0) { + mutex_exit(lock); + return SET_ERROR(EINVAL); + } kn->kn_fop = &pipe_rfiltops; break; case EVFILT_WRITE: + if ((fp->f_flag & FWRITE) == 0) { + mutex_exit(lock); + return SET_ERROR(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); - return (EINVAL); + return SET_ERROR(EINVAL); } kn->kn_hook = pipe; selrecord_knote(&pipe->pipe_sel, kn); mutex_exit(lock); - return (0); + return 0; } /* diff -r c96fe1ab6a49 -r 61050fa7939e tests/kernel/Makefile --- a/tests/kernel/Makefile Sun Sep 20 03:58:19 2026 +0000 +++ b/tests/kernel/Makefile Wed Sep 23 16:15:58 2026 +0000 @@ -73,6 +73,11 @@ 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 LDADD.t_fdrestart+= -lrumpnet_netinet diff -r c96fe1ab6a49 -r 61050fa7939e tests/kernel/t_fdrestart.c --- a/tests/kernel/t_fdrestart.c Sun Sep 20 03:58:19 2026 +0000 +++ b/tests/kernel/t_fdrestart.c Wed Sep 23 16:15:58 2026 +0000 @@ -32,13 +32,21 @@ __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 #include +#include #include #include @@ -86,7 +94,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 +104,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 +212,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 +226,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 +236,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 +350,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)); @@ -181,6 +362,206 @@ 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; +} + +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) +{ + 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) { @@ -201,6 +582,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) { @@ -218,7 +641,314 @@ 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); +} + +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(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); } @@ -242,6 +972,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) { @@ -262,12 +1034,80 @@ 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, 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); + 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); + 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(); diff -r c96fe1ab6a49 -r 61050fa7939e 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 Wed Sep 23 16:15:58 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])); @@ -230,16 +282,31 @@ ATF_TC_HEAD(err, tc) ATF_TC_BODY(err, tc) { struct pollfd pfd; - int fd = 0; + int fd = 0, invalidfd; + int nfds; + char actbuf[128], expbuf[128]; 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=%s expected=%s", + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), POLLNVAL)); } static const char fifo_path[] = "pollhup_fifo"; @@ -276,6 +343,7 @@ ATF_TC_BODY(fifo_inout, tc) long pipe_buf; int ret; ssize_t nwrit, nread; + char eventbuf[128]; fifo_support(); @@ -299,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)); @@ -311,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)); @@ -333,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)); @@ -349,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 @@ -364,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 @@ -378,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)); @@ -403,6 +482,7 @@ ATF_TC_BODY(fifo_hup1, tc) struct pollfd pfd; int rfd, wfd; int ret; + char eventbuf[128]; fifo_support(); @@ -419,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. @@ -455,6 +536,7 @@ ATF_TC_BODY(fifo_hup2, tc) pid_t pid; struct timespec ts1, ts2; int ret; + char eventbuf[128]; fifo_support(); @@ -489,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) @@ -551,11 +634,12 @@ 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 }; struct timespec start, end, delta; + char actbuf[128], expbuf[128]; int nfds; /* @@ -583,14 +667,15 @@ 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); 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. @@ -605,28 +690,43 @@ 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=%s expected=%s" + " POLLHUP=0x%x POLLIN=0x%x POLLOUT=0x%x", + formatpollevents(actbuf, sizeof(actbuf), pfd.revents), + formatpollevents(expbuf, sizeof(expbuf), expected), + POLLHUP, POLLIN, POLLOUT); } 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; int nfds; + char actbuf[128], expbuf[128]; /* * Don't let poll sleep for more than 3sec. (The close delay @@ -659,9 +759,11 @@ check_pollclosed_delayed_read(int readfd 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. @@ -687,27 +789,29 @@ check_pollclosed_delayed_read(int readfd 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 -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; @@ -745,7 +849,7 @@ check_pollclosed_delayed_process(int pol } static void * -check_pollclosed_thread(void *cookie) +check_pollclosedpeer_thread(void *cookie) { int *closefdp = cookie; @@ -756,7 +860,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; @@ -765,7 +869,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). @@ -779,11 +883,12 @@ 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 }; int nfds; + char actbuf[128], expbuf[128]; /* * Close the reader side immediately. @@ -803,9 +908,11 @@ check_pollclosed_immediate_write(int wri 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 @@ -816,10 +923,11 @@ 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; + char actbuf[128], expbuf[128]; /* * Close the writer side immediately. @@ -838,9 +946,11 @@ check_pollclosed_immediate_readnone(int 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. @@ -850,12 +960,13 @@ 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]; ssize_t nread; int nfds; + char actbuf[128], expbuf[128]; /* * Close the writer side immediately. @@ -875,9 +986,11 @@ check_pollclosed_immediate_readsome(int 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 -- @@ -902,9 +1015,11 @@ check_pollclosed_immediate_readsome(int 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. @@ -921,9 +1036,11 @@ check_pollclosed_immediate_readsome(int 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 * @@ -1084,43 +1201,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; @@ -1132,47 +1249,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; @@ -1185,33 +1302,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; @@ -1224,47 +1341,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; @@ -1276,47 +1393,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; @@ -1329,33 +1446,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; @@ -1368,203 +1485,171 @@ 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) -{ - 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_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); -} - -ATF_TC(pollclosed_pipe_immediate_writeempty); -ATF_TC_HEAD(pollclosed_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) -{ - 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_pollclosed_immediate_write(writefd, readfd, POLLOUT, EPIPE); -} - -ATF_TC(pollclosed_pipe_immediate_readsome); -ATF_TC_HEAD(pollclosed_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_writefull, tc) { int writefd, readfd; pollclosed_pipe_setup(&writefd, &readfd); fillpipebuf(writefd); - check_pollclosed_immediate_readsome(readfd, writefd, POLLHUP); + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); } -ATF_TC(pollclosed_pipe_immediate_readnone); -ATF_TC_HEAD(pollclosed_pipe_immediate_readnone, 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_readnone, tc) +ATF_TC_BODY(pollclosedpeer_pipe_immediate_writeempty, tc) +{ + int writefd, readfd; + + pollclosed_pipe_setup(&writefd, &readfd); + /* don't fill pipe buf */ + check_pollclosedpeer_immediate_write(writefd, readfd, POLLOUT, EPIPE); +} + +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(pollclosedpeer_pipe_immediate_readsome, tc) +{ + int writefd, readfd; + + pollclosed_pipe_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedpeer_immediate_readsome(readfd, writefd, POLLHUP); +} + +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(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; - /* - * 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_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; - /* - * 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_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; @@ -1576,16 +1661,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; @@ -1597,16 +1682,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; @@ -1618,17 +1703,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; @@ -1640,17 +1725,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; @@ -1662,17 +1747,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; @@ -1684,47 +1769,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; @@ -1736,16 +1821,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; @@ -1757,32 +1842,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; @@ -1794,33 +1879,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; @@ -1832,256 +1917,855 @@ 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_pollclosedpeer_delayed_process(readfd, writefd, + &check_pollclosedpeer_delayed_read_socket); +} + +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(pollclosedpeer_socketpair1_delayed_thread_write, tc) +{ + int writefd, readfd; + + pollclosed_socketpair1_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedpeer_delayed_thread(writefd, readfd, + &check_pollclosedpeer_delayed_write_fifopipesocket); +} + +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(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); +} + +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); } -ATF_TC(pollclosed_socketpair1_delayed_thread_write); -ATF_TC_HEAD(pollclosed_socketpair1_delayed_thread_write, tc) +/* + * 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 POLLHUP with closing the second half of a socketpair"); + "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(pollclosed_socketpair1_delayed_thread_write, tc) +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_pollclosed_delayed_thread(writefd, readfd, - &check_pollclosed_delayed_write_fifopipesocket); + check_pollclosedself_thread(writefd, POLLOUT); } -ATF_TC(pollclosed_socketpair1_delayed_thread_read); -ATF_TC_HEAD(pollclosed_socketpair1_delayed_thread_read, tc) +ATF_TC(pollclosedself_socketpair1_signal_write); +ATF_TC_HEAD(pollclosedself_socketpair1_signal_write, tc) { atf_tc_set_md_var(tc, "descr", - "Checks POLLHUP with closing the second half of a socketpair"); + "Checks poll on an fd concurrently closed by a signal handler"); } -ATF_TC_BODY(pollclosed_socketpair1_delayed_thread_read, tc) +ATF_TC_BODY(pollclosedself_socketpair1_signal_write, 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); + pollclosed_socketpair1_setup(&writefd, &readfd); + fillpipebuf(writefd); + check_pollclosedself_signal(writefd, POLLOUT); } ATF_TP_ADD_TCS(tp) @@ -2095,69 +2779,104 @@ 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, 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, 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, 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, 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, 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, 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, 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_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, 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, 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, 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, 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, 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, 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, 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); + + 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(); }