NetBSD Problem Report #59578
From www@netbsd.org Wed Aug 6 18:58:36 2025
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits)
client-signature RSA-PSS (2048 bits))
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id EE8A61A923A
for <gnats-bugs@gnats.NetBSD.org>; Wed, 6 Aug 2025 18:58:35 +0000 (UTC)
Message-Id: <20250806185834.428BF1A923C@mollari.NetBSD.org>
Date: Wed, 6 Aug 2025 18:58:34 +0000 (UTC)
From: furkanonder@protonmail.com
Reply-To: furkanonder@protonmail.com
To: gnats-bugs@NetBSD.org
Subject: open() hangs indefinitely on FIFO with frequent signal interruption
X-Send-Pr-Version: www-1.0
>Number: 59578
>Category: kern
>Synopsis: open() hangs indefinitely on FIFO with frequent signal interruption
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: needs-pullups
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Aug 06 19:00:00 +0000 2025
>Closed-Date:
>Last-Modified: Wed Sep 23 19:20:01 +0000 2026
>Originator: Furkan Onder
>Release: NetBSD 10.0
>Organization:
Cpython
>Environment:
NetBSD home.localhost 10.0 NetBSD 10.0 (GENERIC) #0: Thu Mar 28 08:33:33 UTC 2024 mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/amd64/compile/GENERIC amd64
>Description:
This issue was exposed through the CPython test suite, specifically the `test_eintr` module's `SocketEINTRTest.test_os_open` test,
which hangs indefinitely on NetBSD 10.0 (see https://github.com/python/cpython/issues/137397).
The underlying problem is that when attempting to open a FIFO for writing (O_WRONLY) while the process receives frequent signals,
the open() system call gets repeatedly interrupted with EINTR and never completes successfully, even when a reader process is
available on the other end.
This creates an infinite loop where the process hangs indefinitely, continuously retrying the open() call which always returns EINTR.
>How-To-Repeat:
Save the following C program as `reproducer.c`
```
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
volatile sig_atomic_t signal_count = 0;
/**
* Signal handler for SIGALRM.
*/
void handle_signal(int sig) {
(void)sig; // Suppress unused parameter warning
signal_count++;
}
/**
* Retry open() if it's interrupted by a signal (EINTR).
*/
int safe_open(const char *path, int flags) {
int fd;
while ((fd = open(path, flags)) < 0) {
if (errno != EINTR) {
perror("open");
exit(EXIT_FAILURE);
}
write(STDOUT_FILENO, ".", 1);
}
return fd;
}
/**
* Retry close() if it's interrupted by a signal (EINTR).
*/
int safe_close(int fd) {
int ret;
while ((ret = close(fd)) < 0) {
if (errno != EINTR) {
perror("close");
return ret;
}
write(STDOUT_FILENO, "C", 1);
}
return ret;
}
/**
* Sleep for a specified number of milliseconds.
*/
void sleep_ms(long ms) {
struct timespec ts;
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms % 1000) * 1000000L;
nanosleep(&ts, NULL);
}
/**
* Set up a timer to send SIGALRM every 10 milliseconds.
*/
void setup_timer(void) {
struct sigaction sa = {0};
sa.sa_handler = handle_signal;
sigaction(SIGALRM, &sa, NULL);
struct itimerval timer = {
.it_value = {0, 10000}, // Start after 10ms
.it_interval = {0, 10000} // Repeat every 10ms
};
if (setitimer(ITIMER_REAL, &timer, NULL) < 0) {
perror("setitimer");
exit(EXIT_FAILURE);
}
}
int main() {
printf("EINTR test - Ctrl+C to stop\n");
setup_timer();
for (int i = 1; i <= 50; ++i) {
char fifo[64];
snprintf(fifo, sizeof(fifo), "/tmp/test_fifo_%d", i);
unlink(fifo);
if (mkfifo(fifo, 0666) < 0) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
// Child opens FIFO for reading
sleep_ms(50); // 50ms delay to let parent open writer
int fd = safe_open(fifo, O_RDONLY);
safe_close(fd);
exit(EXIT_SUCCESS);
}
else {
// Parent opens FIFO for writing
int fd = safe_open(fifo, O_WRONLY);
safe_close(fd);
wait(NULL);
unlink(fifo);
printf("Loop %d OK (signals: %d)\n", i, signal_count);
signal_count = 0;
}
sleep_ms(1); // Small pause before next iteration
}
printf("Test complete.\n");
return EXIT_SUCCESS;
}
```
Compile and run:
```
gcc -o reproducer reproducer.c
./reproducer
```
The program should complete successfully with output like:
```
EINTR test - Ctrl+C to stop
.....Loop 1 OK (signals: 5)
.....Loop 2 OK (signals: 5)
.....Loop 3 OK (signals: 5)
.....Loop 4 OK (signals: 5)
.....Loop 5 OK (signals: 5)
.....Loop 6 OK (signals: 5)
.....Loop 7 OK (signals: 6)
.....Loop 8 OK (signals: 5)
.....Loop 9 OK (signals: 5)
.....Loop 10 OK (signals: 5)
.....Loop 11 OK (signals: 5)
.....Loop 12 OK (signals: 5)
.....Loop 13 OK (signals: 5)
.....Loop 14 OK (signals: 6)
.....Loop 15 OK (signals: 5)
.....Loop 16 OK (signals: 5)
.....Loop 17 OK (signals: 5)
.....Loop 18 OK (signals: 5)
.....Loop 19 OK (signals: 5)
.....Loop 20 OK (signals: 5)
.....Loop 21 OK (signals: 6)
.....Loop 22 OK (signals: 5)
.....Loop 23 OK (signals: 5)
.....Loop 24 OK (signals: 5)
.....Loop 25 OK (signals: 5)
.....Loop 26 OK (signals: 5)
.....Loop 27 OK (signals: 5)
.....Loop 28 OK (signals: 6)
.....Loop 29 OK (signals: 5)
.....Loop 30 OK (signals: 5)
.....Loop 31 OK (signals: 5)
.....Loop 32 OK (signals: 5)
.....Loop 33 OK (signals: 5)
.....Loop 34 OK (signals: 5)
.....Loop 35 OK (signals: 6)
.....Loop 36 OK (signals: 5)
.....Loop 37 OK (signals: 5)
.....Loop 38 OK (signals: 5)
.....Loop 39 OK (signals: 5)
.....Loop 40 OK (signals: 5)
.....Loop 41 OK (signals: 5)
.....Loop 42 OK (signals: 6)
.....Loop 43 OK (signals: 5)
.....Loop 44 OK (signals: 5)
.....Loop 45 OK (signals: 5)
.....Loop 46 OK (signals: 5)
.....Loop 47 OK (signals: 5)
.....Loop 48 OK (signals: 5)
.....Loop 49 OK (signals: 6)
.....Loop 50 OK (signals: 5)
Test complete.
```
The program hangs indefinitely:
```
EINTR test - Ctrl+C to stop
...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................^C
```
```
ktrace -f trace.out ./reproducer
# After hanging, press Ctrl+C and examine trace
kdump -f trace.out | tail -50
```
Output:
```
17368 17368 reproducer CALL setcontext(0x7f7fff444000)
17368 17368 reproducer RET setcontext JUSTRETURN
17368 17368 reproducer CALL write(1,0x401173,1)
17368 17368 reproducer GIO fd 1 wrote 1 bytes
"."
17368 17368 reproducer RET write 1
17368 17368 reproducer CALL open(0x7f7fff4443d0,1,1)
17368 17368 reproducer NAMI "/tmp/test_fifo_1"
17368 17368 reproducer RET open -1 errno 4 Interrupted system call
17368 17368 reproducer PSIG SIGALRM caught handler=0x400dba mask=(): code=SI_TIMER sent by pid=0, uid=0 with sigval 0x0)
17368 17368 reproducer CALL setcontext(0x7f7fff444000)
17368 17368 reproducer RET setcontext JUSTRETURN
17368 17368 reproducer CALL write(1,0x401173,1)
17368 17368 reproducer GIO fd 1 wrote 1 bytes
"."
17368 17368 reproducer RET write 1
17368 17368 reproducer CALL open(0x7f7fff4443d0,1,1)
17368 17368 reproducer NAMI "/tmp/test_fifo_1"
17368 17368 reproducer RET open -1 errno 4 Interrupted system call
17368 17368 reproducer PSIG SIGALRM caught handler=0x400dba mask=(): code=SI_TIMER sent by pid=0, uid=0 with sigval 0x0)
17368 17368 reproducer CALL setcontext(0x7f7fff444000)
17368 17368 reproducer RET setcontext JUSTRETURN
17368 17368 reproducer CALL write(1,0x401173,1)
17368 17368 reproducer GIO fd 1 wrote 1 bytes
"."
17368 17368 reproducer RET write 1
17368 17368 reproducer CALL open(0x7f7fff4443d0,1,1)
17368 17368 reproducer NAMI "/tmp/test_fifo_1"
17368 17368 reproducer RET open -1 errno 4 Interrupted system call
17368 17368 reproducer PSIG SIGALRM caught handler=0x400dba mask=(): code=SI_TIMER sent by pid=0, uid=0 with sigval 0x0)
17368 17368 reproducer CALL setcontext(0x7f7fff444000)
17368 17368 reproducer RET setcontext JUSTRETURN
17368 17368 reproducer CALL write(1,0x401173,1)
17368 17368 reproducer GIO fd 1 wrote 1 bytes
"."
17368 17368 reproducer RET write 1
17368 17368 reproducer CALL open(0x7f7fff4443d0,1,1)
17368 17368 reproducer NAMI "/tmp/test_fifo_1"
17368 17368 reproducer RET open -1 errno 4 Interrupted system call
17368 17368 reproducer PSIG SIGALRM caught handler=0x400dba mask=(): code=SI_TIMER sent by pid=0, uid=0 with sigval 0x0)
17368 17368 reproducer CALL setcontext(0x7f7fff444000)
17368 17368 reproducer RET setcontext JUSTRETURN
17368 17368 reproducer CALL write(1,0x401173,1)
17368 17368 reproducer GIO fd 1 wrote 1 bytes
"."
17368 17368 reproducer RET write 1
17368 17368 reproducer CALL open(0x7f7fff4443d0,1,1)
17368 17368 reproducer NAMI "/tmp/test_fifo_1"
17368 17368 reproducer RET open RESTART
17368 17368 reproducer PSIG SIGINT SIG_DFL: code=SI_NOINFO
```
>Fix:
>Release-Note:
>Audit-Trail:
From: RVP <rvp@SDF.ORG>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/59578: open() hangs indefinitely on FIFO with frequent
signal interruption
Date: Thu, 7 Aug 2025 07:07:55 +0000 (UTC)
On Wed, 6 Aug 2025, furkanonder@protonmail.com wrote:
> The underlying problem is that when attempting to open a FIFO for writing (O_WRONLY) while the process receives frequent signals,
> the open() system call gets repeatedly interrupted with EINTR and never completes successfully, even when a reader process is
> available on the other end.
>
It almost always isn't as per this reproducer because the child sleeps before
opening the read end (rather than the writer--parent). For blocking FIFOs,
there has to be a reader first, before an open(O_WRONLY) returns.
Additionally, the child isn't even doing a read (and the parent doing a write)
to synchronize things. The child just closes the FIFO immediately. This just
makes the reproducer _very_ racy.
Put the sleep in the parent (in lieu of a read/write sync) and the reproducer
works as expected.
> sleep_ms(50); // 50ms delay to let parent open writer
The other issues are with the nanosleep() and wait(). With the timer firing
every 10ms, these _will_ get interrupted and must be retried if errno == EINTR.
-RVP
From: mlelstv@serpens.de (Michael van Elst)
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/59578: open() hangs indefinitely on FIFO with frequent signal interruption
Date: Thu, 7 Aug 2025 07:32:11 -0000 (UTC)
furkanonder@protonmail.com writes:
>The underlying problem is that when attempting to open a FIFO for writing (O_WRONLY) while the process receives frequent signals,
>the open() system call gets repeatedly interrupted with EINTR and never completes successfully, even when a reader process is
>available on the other end.
Actually the reproducer is bogus as the nanosleep and more important
the wait system calls are also interrupted by the signal (and not
retried).
But if you handle all this, there still seems to be a bug.
What happens is that:
- the parent opens the fifo and gets blocked
- the child opens the fifo and succeeds, waking the parent
- the parent gets interrupted before resuming and finishing the
open system call, so open fails with EINTR.
- the child closes the fifo and exits.
- your code retries the open operation of the parent and waits
forever for the child that is already gone.
I'm not sure if that is an allowed scenario, but it would probably be
better if the interrupted open call would still succeed when the
reason for blocking is gone at the same time.
An alternative would be to only allow the peer to proceed when
the open system call returns success. But that synchronisation
is more complex.
From: furkanonder <furkanonder@protonmail.com>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: kern/59578: open() hangs indefinitely on FIFO with frequent signal interruption
Date: Thu, 07 Aug 2025 20:41:49 +0000
Sent with Proton Mail secure email.
On Thursday, August 7th, 2025 at 10:35 AM, Michael van Elst via gnats <gnat=
s-admin@NetBSD.org> wrote:
> The following reply was made to PR kern/59578; it has been noted by GNATS=
.
>=20
> From: mlelstv@serpens.de (Michael van Elst)
> To: gnats-bugs@netbsd.org
> Cc:
> Subject: Re: kern/59578: open() hangs indefinitely on FIFO with frequent =
signal interruption
> Date: Thu, 7 Aug 2025 07:32:11 -0000 (UTC)
>=20
> furkanonder@protonmail.com writes:
>=20
> > The underlying problem is that when attempting to open a FIFO for writi=
ng (O_WRONLY) while the process receives frequent signals,
>=20
> > the open() system call gets repeatedly interrupted with EINTR and never=
completes successfully, even when a reader process is
>=20
> > available on the other end.
>=20
>=20
>=20
> Actually the reproducer is bogus as the nanosleep and more important
> the wait system calls are also interrupted by the signal (and not
> retried).
>=20
> But if you handle all this, there still seems to be a bug.
>=20
> What happens is that:
>=20
> - the parent opens the fifo and gets blocked
> - the child opens the fifo and succeeds, waking the parent
> - the parent gets interrupted before resuming and finishing the
> open system call, so open fails with EINTR.
> - the child closes the fifo and exits.
> - your code retries the open operation of the parent and waits
> forever for the child that is already gone.
>=20
>=20
> I'm not sure if that is an allowed scenario, but it would probably be
> better if the interrupted open call would still succeed when the
> reason for blocking is gone at the same time.
>=20
> An alternative would be to only allow the peer to proceed when
> the open system call returns success. But that synchronisation
> is more complex.
Thank you Michael for the detailed analysis. You've correctly identified th=
e core kernel issue.
While I acknowledge the race conditions in my reproducer (nanosleep/wait EI=
NTR handling), I respectfully disagree that it's fundamentally "bogus." The=
reproducer demonstrates a real kernel bug affecting production software:
It exposes the exact issue found in CPython's test suite - derived from Pyt=
hon's test_eintr module, which runs successfully on other Unix systems.
Similar reproducers exist for other systems - FreeBSD had an analogous issu=
e (https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D203162) with "a C pr=
ogram based on the Python unit test."
The race conditions don't invalidate the core issue - Even with proper EINT=
R handling, the fundamental bug you identified still exists: when a parent'=
s open() gets interrupted after the child opens/closes the FIFO, the retry =
hangs indefinitely.
Your analysis pinpoints the exact problem: "the parent gets interrupted bef=
ore resuming and finishing the open system call, so open fails with EINTR. =
The child closes the fifo and exits. Your code retries the open operation o=
f the parent and waits forever for the child that is already gone."
This breaks Python's test suite on NetBSD while working correctly on Linux,=
FreeBSD, and other Unix systems.
I agree with your first proposed solution: allow interrupted open() to succ=
eed when the blocking condition is resolved. This aligns better with user e=
xpectations and POSIX semantics.
State-Changed-From-To: open->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Tue, 22 Sep 2026 13:35:07 +0000
State-Changed-Why:
fixed in HEAD, needs pullup-11, pullup-10 after the testbed runs
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59578 CVS commit: src
Date: Tue, 22 Sep 2026 13:34:00 +0000
Module Name: src
Committed By: riastradh
Date: Tue Sep 22 13:34:00 UTC 2026
Modified Files:
src/sys/miscfs/fifofs: fifo_vnops.c
src/tests/lib/libc/sys: t_mkfifo.c
Log Message:
fifo: Fix race between open and signal.
The problem is:
thread A thread B
-------- --------
enter open("fifo", O_WRONLY)
-> fi_writers++
-> sleep since fi_readers == 0
enter open("fifo", O_RDONLY)
-> fi_readers++
-> succeed since fi_writers > 0
open succeeds with fd
enter close(fd)
-> fi_readers--
close succeeds
*SIGALRM*
-> fi_writers--
open fails with EINTR
At this point, a reader open() has succeeded with no corresponding
writer open() success, and if the writer retries, it will hang
because fi_readers is back to zero. Instead, when the reader open()
succeeds, it should cause the writer open() to succeed as well (and
handle the signal afterward).
Solution: create a generation number that is advanced whenever a
reader successfully opens; any writers will succeed on wakeup if the
read generation has advanced since they went to sleep.
thread A thread B
-------- --------
enter open("fifo", O_WRONLY)
-> fi_writers++
-> save rgen = fi_rgen = 0
-> sleep since fi_readers == 0
enter open("fifo", O_RDONLY)
-> fi_readers++
-> succeed since fi_writers > 0
-> fi_rgen++
open succeeds with fd
enter close(fd)
-> fi_readers--
close succeeds
*SIGALRM*
-> succeed since 0 = rgen != fi_rgen = 1
open succeeds with fd
Same problem occurs in with the reader/writer roles reversed; same
solution in the other direction, with a writer generation number.
PR kern/59578: open() hangs indefinitely on FIFO with frequent signal
interruption
Related bugs:
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=203162
https://bugs.python.org/issue25122
https://github.com/python/cpython/issues/137397
Implementation based on FreeBSD rev. 288044 by Konstantin Belousov:
https://svnweb.freebsd.org/base?view=revision&revision=288044
I chose to use uint64_t generation numbers so that we don't have to
think about overflow from counting.
To generate a diff of this commit:
cvs rdiff -u -r1.91 -r1.92 src/sys/miscfs/fifofs/fifo_vnops.c
cvs rdiff -u -r1.5 -r1.6 src/tests/lib/libc/sys/t_mkfifo.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59578 CVS commit: src/tests/lib/libc/sys
Date: Tue, 22 Sep 2026 13:32:24 +0000
Module Name: src
Committed By: riastradh
Date: Tue Sep 22 13:32:24 UTC 2026
Modified Files:
src/tests/lib/libc/sys: t_mkfifo.c
Log Message:
t_mkfifo: Add test for PR kern/59578.
The problem is:
thread A thread B
-------- --------
enter open("fifo", O_WRONLY)
-> fi_writers++
-> sleep since fi_readers == 0
enter open("fifo", O_RDONLY)
-> fi_readers++
-> return since fi_writers > 0
open succeeds with fd
enter close(fd)
-> fi_readers--
close succeeds
*SIGALRM*
-> fi_writers--
open fails with EINTR
At this point, a reader open() has succeeded with no corresponding
writer open() success, and if the writer retries, it will hang
because fi_readers is back to zero. Instead, when the reader open()
succeeds, it should cause the writer open() to succeed as well (and
handle the signal afterward).
Same problem occurs in with the reader/writer roles reversed.
PR kern/59578: open() hangs indefinitely on FIFO with frequent signal
interruption
Related bugs:
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=203162
https://bugs.python.org/issue25122
https://github.com/python/cpython/issues/137397
To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/tests/lib/libc/sys/t_mkfifo.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59578 CVS commit: src/tests/lib/libc/sys
Date: Wed, 23 Sep 2026 19:19:40 +0000
Module Name: src
Committed By: riastradh
Date: Wed Sep 23 19:19:40 UTC 2026
Modified Files:
src/tests/lib/libc/sys: t_mkfifo.c
Log Message:
t_mkfifo: Bump timeouts a little bit to exercise the race better.
Currently on the releng testbed, the parent's open() call is not
failing with _any_ EINTR, presumably because (a) the child is opening
the fifo before any itimer alarms fire and then (b) signal no longer
causes the parent's open() to fail once the child has opened the other
end of the fifo. So let's give the parent a bit more of a head start,
and run the race a little longer.
PR kern/59578: open() hangs indefinitely on FIFO with frequent signal
interruption
To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/tests/lib/libc/sys/t_mkfifo.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.