Bug report
Bug description:
The SignalEINTRTest.test_sigwaitinfo test in test_eintr fails on NetBSD. According to the official POSIX.1-2024 specification for sigwaitinfo:
"The sigtimedwait() and sigwaitinfo() functions may fail if: [EINTR] The wait was interrupted by an unblocked, caught signal."
NetBSD's sigwaitinfo() implementation violates POSIX by returning errno = ECANCELED (87) instead of errno = EINTR (4) when interrupted by a signal not in the wait set.
Configuration
./configure --with-pydebug
Test
╰─$ ./python -m unittest test._test_eintr.SignalEINTRTest.test_sigwaitinfo -v test_sigwaitinfo (test._test_eintr.SignalEINTRTest.test_sigwaitinfo) ... ERROR ====================================================================== ERROR: test_sigwaitinfo (test._test_eintr.SignalEINTRTest.test_sigwaitinfo) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/blue/Desktop/cpython/Lib/test/_test_eintr.py", line 440, in test_sigwaitinfo self.check_sigwait(wait_func) ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^ File "/home/blue/Desktop/cpython/Lib/test/_test_eintr.py", line 430, in check_sigwait wait_func(signum) ~~~~~~~~~^^^^^^^^ File "/home/blue/Desktop/cpython/Lib/test/_test_eintr.py", line 438, in wait_func signal.sigwaitinfo([signum]) ~~~~~~~~~~~~~~~~~~^^^^^^^^^^ OSError: [Errno 87] Operation canceled ---------------------------------------------------------------------- Ran 1 test in 0.112s FAILED (errors=1)
Reproduction
I created a minimal C program that reproduces the same issue.
#include <signal.h> #include <pthread.h> #include <unistd.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> volatile sig_atomic_t got_sigalrm = 0; void sigalrm_handler(int sig) { got_sigalrm = 1; printf("SIGALRM handler called\n"); } int main() { printf("sigwaitinfo interruption test\n"); sigset_t sigset; siginfo_t info; int target_signal = SIGUSR1; // Set SIGALRM handler struct sigaction sa = {0}; sa.sa_handler = sigalrm_handler; sigaction(SIGALRM, &sa, NULL); // Block SIGUSR1 and save old mask sigemptyset(&sigset); sigaddset(&sigset, target_signal); pthread_sigmask(SIG_BLOCK, &sigset, NULL); printf("Blocked SIGUSR1, waiting with sigwaitinfo...\n"); pid_t pid = fork(); if (pid == 0) { sleep(1); printf("Child: sending SIGALRM to interrupt parent\n"); kill(getppid(), SIGALRM); exit(0); } printf("Parent: calling sigwaitinfo([SIGUSR1])...\n"); int result = sigwaitinfo(&sigset, &info); if (result == -1) { printf("sigwaitinfo failed: errno = %d (%s)\n", errno, strerror(errno)); } printf("SIGALRM handler was called: %s\n", got_sigalrm ? "yes" : "no"); int status; waitpid(pid, &status, 0); printf("Child exited with status: %d\n", WEXITSTATUS(status)); return 0; }
Output on NetBSD 10.0:
sigwaitinfo interruption test
Blocked SIGUSR1, waiting with sigwaitinfo...
Parent: calling sigwaitinfo([SIGUSR1])...
Child: sending SIGALRM to interrupt parent
SIGALRM handler called
sigwaitinfo failed: errno = 87 (Operation canceled)
SIGALRM handler was called: yes
Child exited with status: 0
Expected POSIX-compliant output (Linux):
sigwaitinfo interruption test
Blocked SIGUSR1, waiting with sigwaitinfo...
Parent: calling sigwaitinfo([SIGUSR1])...
Child: sending SIGALRM to interrupt parent
SIGALRM handler called
sigwaitinfo failed: errno = 4 (Interrupted system call)
SIGALRM handler was called: yes
Child exited with status: 0
CPython versions tested on:
CPython main branch, 3.15, 3.14, 3.13
Operating systems tested on:
Other