bpo-37811: FreeBSD, OSX: fix poll(2) usage in sockets module (GH-15202)

FreeBSD implementation of poll(2) restricts the timeout argument to be
either zero, or positive, or equal to INFTIM (-1).

Unless otherwise overridden, socket timeout defaults to -1. This value
is then converted to milliseconds (-1000) and used as argument to the
poll syscall. poll returns EINVAL (22), and the connection fails.

This bug was discovered during the EINTR handling testing, and the
reproduction code can be found in
https://bugs.python.org/issue23618 (see connect_eintr.py,
attached). On GNU/Linux, the example runs as expected.

This change is trivial:
If the supplied timeout value is negative, truncate it to -1.
This commit is contained in:
Artem Khramov 2019-08-15 03:21:48 +06:00 committed by Victor Stinner
parent dcfe111eb5
commit 2814620657
3 changed files with 16 additions and 0 deletions

View File

@ -837,6 +837,7 @@ Lawrence Kesteloot
Garvit Khatri
Vivek Khera
Dhiru Kholia
Artem Khramov
Akshit Khurana
Sanyam Khurana
Mads Kiilerich

View File

@ -0,0 +1,4 @@
Fix ``socket`` module's ``socket.connect(address)`` function being unable to
establish connection in case of interrupted system call. The problem was
observed on all OSes which ``poll(2)`` system call can take only
non-negative integers and -1 as a timeout value.

View File

@ -789,6 +789,17 @@ internal_select(PySocketSockObject *s, int writing, _PyTime_t interval,
ms = _PyTime_AsMilliseconds(interval, _PyTime_ROUND_CEILING);
assert(ms <= INT_MAX);
/* On some OSes, typically BSD-based ones, the timeout parameter of the
poll() syscall, when negative, must be exactly INFTIM, where defined,
or -1. See issue 37811. */
if (ms < 0) {
#ifdef INFTIM
ms = INFTIM;
#else
ms = -1;
#endif
}
Py_BEGIN_ALLOW_THREADS;
n = poll(&pollfd, 1, (int)ms);
Py_END_ALLOW_THREADS;