NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/60851: change in pipe kevent EVFILT_READ/WRITE on wrong end
>Number: 60851
>Category: kern
>Synopsis: change in pipe kevent EVFILT_READ/WRITE on wrong end
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Oct 05 19:10:01 +0000 2026
>Originator: Taylor R Campbell
>Release: current
>Organization:
Acme Pipefitters, Inc.
>Environment:
>Description:
Before my recent flurry of changes to the pipe(2)
implementation, kevent EVFILT_READ on the write end of a pipe,
and kevent EVFILT_WRITE on the read end of a pipe, would
quietly be accepted.
After the changes, specifically after
https://mail-index.netbsd.org/source-changes/2026/10/03/msg164422.html,
kevent would reject these with EINVAL.
1208 switch (kn->kn_filter) {
1209 case EVFILT_READ:
1210 if ((fp->f_flag & FREAD) == 0) {
1211 mutex_exit(lock);
1212 return (EINVAL);
1213 }
1214 kn->kn_fop = &pipe_rfiltops;
1215 break;
1216 case EVFILT_WRITE:
1217 if ((fp->f_flag & FWRITE) == 0) {
1218 mutex_exit(lock);
1219 return (EINVAL);
1220 }
1221 kn->kn_fop = &pipe_wfiltops;
1222 break;
1223 default:
1224 mutex_exit(lock);
1225 return (EINVAL);
1226 }
https://nxr.netbsd.org/xref/src/sys/kern/sys_pipe.c?r=1.174#1208
This is apparently in conflict with FreeBSD:
/*
* If a filter is requested that is not supported by this file
* descriptor, don't return an error, but also don't ever generate an
* event.
*/
if ((kn->kn_filter == EVFILT_READ) && !(fp->f_flag & FREAD)) {
kn->kn_fop = &pipe_nfiltops;
return (0);
}
if ((kn->kn_filter == EVFILT_WRITE) && !(fp->f_flag & FWRITE)) {
kn->kn_fop = &pipe_nfiltops;
return (0);
}
https://cgit.freebsd.org/src/tree/sys/kern/sys_pipe.c?h=release/15.1.0-p4&id=e38a7085f3a8ecd317c947591fe42b5ca9ab317b#n1791
The flurry of changes appear to have broken dovecot:
https://mail-index.netbsd.org/current-users/2026/10/05/msg047953.html
Review of a ktrace shows that SIGABRT happens shortly after
kevent(2) fails with EINVAL, which is consistent with this code
path showing that dovecot assumes EVFILT_READ on the write end
of a pipe will work when just the (dovecot-internal) event
condition IO_ERROR is requested:
if ((io->io.condition & (IO_READ | IO_ERROR)) != 0) {
MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_ADD, 0, 0, io);
if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
i_panic("kevent(EV_ADD, READ, %d) failed: %m", io->fd);
}
if ((io->io.condition & IO_WRITE) != 0) {
MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_ADD, 0, 0, io);
if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0)
i_panic("kevent(EV_ADD, WRITE, %d) failed: %m", io->fd);
}
https://github.com/dovecot/core/blob/2.3.21.1/src/lib/ioloop-kqueue.c#L64-L73
/* start listening errors for status fd, it means master died */
service->io_status_error = io_add(MASTER_DEAD_FD, IO_ERROR,
master_status_error, service);
https://github.com/dovecot/core/blob/2.3.21.1/src/lib-master/master-service.c#L688-L690
It's not clear that this isn't a dovecot bug, but there was
obviously an unexpected change in the NetBSD semantics which
now disagrees with FreeBSD.
>How-To-Repeat:
run mail/dovecot2
>Fix:
Replace these error branches by a null filterops that never
returns ready, like FreeBSD does -- and if we really want the
EINVAL semantics (which I tossed in without thinking much about
it just to detect what I assumed must be mistakes), that should
be done in a spearate intentional commit with supporting
documentation.
Home |
Main Index |
Thread Index |
Old Index