diff --git a/Include/fileobject.h b/Include/fileobject.h index c4a2a2bb15c..a99c94d2d54 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -44,6 +44,13 @@ int _PyVerify_fd(int fd); #endif #endif /* Py_LIMITED_API */ +/* A routine to check if a file descriptor can be select()-ed. */ +#ifdef HAVE_SELECT + #define _PyIsSelectable_fd(FD) (((FD) >= 0) && ((FD) < FD_SETSIZE)) +#else + #define _PyIsSelectable_fd(FD) (1) +#endif /* HAVE_SELECT */ + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS b/Misc/NEWS index b852219d651..17332914de1 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -268,6 +268,9 @@ Core and Builtins Library ------- +- Issue #12287: Fix a stack corruption in ossaudiodev module when the FD is + greater than FD_SETSIZE. + - Issue #12839: Fix crash in zlib module due to version mismatch. Fix by Richard M. Tew. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index b203ce42625..3d44b61c9f5 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1040,10 +1040,8 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing) #endif /* Guard against socket too large for select*/ -#ifndef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE - if (s->sock_fd >= FD_SETSIZE) + if (!_PyIsSelectable_fd(s->sock_fd)) return SOCKET_TOO_LARGE_FOR_SELECT; -#endif /* Construct the arguments to select */ tv.tv_sec = (int)s->sock_timeout; diff --git a/Modules/ossaudiodev.c b/Modules/ossaudiodev.c index 15057314497..95a23b7ea34 100644 --- a/Modules/ossaudiodev.c +++ b/Modules/ossaudiodev.c @@ -474,6 +474,11 @@ oss_writeall(oss_audio_t *self, PyObject *args) if (!PyArg_ParseTuple(args, "y#:write", &cp, &size)) return NULL; + if (!_PyIsSelectable_fd(self->fd)) { + PyErr_SetString(PyExc_ValueError, + "file descriptor out of range for select"); + return NULL; + } /* use select to wait for audio device to be available */ FD_ZERO(&write_set_fds); FD_SET(self->fd, &write_set_fds); diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 20f23d92603..e594b51f23e 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -110,7 +110,7 @@ seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) #if defined(_MSC_VER) max = 0; /* not used for Win32 */ #else /* !_MSC_VER */ - if (v < 0 || v >= FD_SETSIZE) { + if (!_PyIsSelectable_fd(v)) { PyErr_SetString(PyExc_ValueError, "filedescriptor out of range in select()"); goto finally; @@ -160,13 +160,6 @@ set2list(fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) for (j = 0; fd2obj[j].sentinel >= 0; j++) { fd = fd2obj[j].fd; if (FD_ISSET(fd, set)) { -#ifndef _MSC_VER - if (fd > FD_SETSIZE) { - PyErr_SetString(PyExc_SystemError, - "filedescriptor out of range returned in select()"); - goto finally; - } -#endif o = fd2obj[j].obj; fd2obj[j].obj = NULL; /* transfer ownership */ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 936a68dcd6e..f56e9afd52d 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -485,18 +485,14 @@ static PyTypeObject sock_type; #define SOCKLEN_T_LIMIT INT_MAX #endif -#ifdef Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE -/* Platform can select file descriptors beyond FD_SETSIZE */ -#define IS_SELECTABLE(s) 1 -#elif defined(HAVE_POLL) +#ifdef HAVE_POLL /* Instead of select(), we'll use poll() since poll() works on any fd. */ #define IS_SELECTABLE(s) 1 /* Can we call select() with this socket without a buffer overrun? */ #else -/* POSIX says selecting file descriptors beyond FD_SETSIZE - has undefined behaviour. If there's no timeout left, we don't have to - call select, so it's a safe, little white lie. */ -#define IS_SELECTABLE(s) ((s)->sock_fd < FD_SETSIZE || s->sock_timeout <= 0.0) +/* If there's no timeout left, we don't have to call select, so it's a safe, + * little white lie. */ +#define IS_SELECTABLE(s) (_PyIsSelectable_fd((s)->sock_fd) || (s)->sock_timeout <= 0.0) #endif static PyObject*