Bug #876637, prevent stack corruption when socket descriptor
is larger than FD_SETSIZE. This can only be acheived with ulimit -n SOME_NUMBER_BIGGER_THAN_FD_SETSIZE which is typically only available to root. Since this wouldn't normally be run in a test (ie, run as root), it doesn't seem too worthwhile to add a normal test. The bug report has one version of a test. I've written another. Not sure what the best thing to do is. Do the check before calling internal_select() because we can't set an error in between Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS. This seemed the clearest solution, ie handle before calling internal_select() rather than inside. Plus there is at least one place outside of internal_select() that needed to be handled. Will backport.
This commit is contained in:
parent
19cbcad20e
commit
082b2df33f
@ -216,6 +216,9 @@ Core and builtins
|
|||||||
Extension Modules
|
Extension Modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Bug #876637, prevent stack corruption when socket descriptor
|
||||||
|
is larger than FD_SETSIZE.
|
||||||
|
|
||||||
- Patch #1407135, bug #1424041: harmonize mmap behavior of anonymous memory.
|
- Patch #1407135, bug #1424041: harmonize mmap behavior of anonymous memory.
|
||||||
mmap.mmap(-1, size) now returns anonymous memory in both Unix and Windows.
|
mmap.mmap(-1, size) now returns anonymous memory in both Unix and Windows.
|
||||||
mmap.mmap(0, size) should not be used on Windows for anonymous memory.
|
mmap.mmap(0, size) should not be used on Windows for anonymous memory.
|
||||||
|
@ -74,6 +74,7 @@ typedef enum {
|
|||||||
SOCKET_IS_BLOCKING,
|
SOCKET_IS_BLOCKING,
|
||||||
SOCKET_HAS_TIMED_OUT,
|
SOCKET_HAS_TIMED_OUT,
|
||||||
SOCKET_HAS_BEEN_CLOSED,
|
SOCKET_HAS_BEEN_CLOSED,
|
||||||
|
SOCKET_INVALID,
|
||||||
SOCKET_OPERATION_OK
|
SOCKET_OPERATION_OK
|
||||||
} timeout_state;
|
} timeout_state;
|
||||||
|
|
||||||
@ -272,6 +273,9 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file)
|
|||||||
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
||||||
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
|
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
} else if (sockstate == SOCKET_INVALID) {
|
||||||
|
PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
|
||||||
|
goto fail;
|
||||||
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
|
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -372,6 +376,10 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
|
|||||||
if (s->sock_fd < 0)
|
if (s->sock_fd < 0)
|
||||||
return SOCKET_HAS_BEEN_CLOSED;
|
return SOCKET_HAS_BEEN_CLOSED;
|
||||||
|
|
||||||
|
/* Guard against socket too large for select*/
|
||||||
|
if (s->sock_fd >= FD_SETSIZE)
|
||||||
|
return SOCKET_INVALID;
|
||||||
|
|
||||||
/* Construct the arguments to select */
|
/* Construct the arguments to select */
|
||||||
tv.tv_sec = (int)s->sock_timeout;
|
tv.tv_sec = (int)s->sock_timeout;
|
||||||
tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6);
|
tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6);
|
||||||
@ -409,6 +417,9 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
|
|||||||
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
|
||||||
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
|
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
} else if (sockstate == SOCKET_INVALID) {
|
||||||
|
PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
err = 0;
|
err = 0;
|
||||||
@ -467,6 +478,9 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
|
|||||||
PyErr_SetString(PySSLErrorObject, "The read operation timed out");
|
PyErr_SetString(PySSLErrorObject, "The read operation timed out");
|
||||||
Py_DECREF(buf);
|
Py_DECREF(buf);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
} else if (sockstate == SOCKET_INVALID) {
|
||||||
|
PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -395,6 +395,16 @@ static int taskwindow;
|
|||||||
there has to be a circular reference. */
|
there has to be a circular reference. */
|
||||||
static PyTypeObject sock_type;
|
static PyTypeObject sock_type;
|
||||||
|
|
||||||
|
/* Can we call select() with this socket without a buffer overrun? */
|
||||||
|
#define IS_SELECTABLE(s) ((s)->sock_fd < FD_SETSIZE)
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
select_error(void)
|
||||||
|
{
|
||||||
|
PyErr_SetString(socket_error, "unable to select on socket");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Convenience function to raise an error according to errno
|
/* Convenience function to raise an error according to errno
|
||||||
and return a NULL pointer from a function. */
|
and return a NULL pointer from a function. */
|
||||||
|
|
||||||
@ -1408,6 +1418,9 @@ sock_accept(PySocketSockObject *s)
|
|||||||
newfd = -1;
|
newfd = -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!IS_SELECTABLE(s))
|
||||||
|
return select_error();
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
timeout = internal_select(s, 0);
|
timeout = internal_select(s, 0);
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
@ -1736,7 +1749,8 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
|
|||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
|
|
||||||
if (s->sock_timeout > 0.0) {
|
if (s->sock_timeout > 0.0) {
|
||||||
if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK) {
|
if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK &&
|
||||||
|
IS_SELECTABLE(s)) {
|
||||||
/* This is a mess. Best solution: trust select */
|
/* This is a mess. Best solution: trust select */
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
fd_set fds_exc;
|
fd_set fds_exc;
|
||||||
@ -1781,7 +1795,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
if (s->sock_timeout > 0.0) {
|
if (s->sock_timeout > 0.0) {
|
||||||
if (res < 0 && errno == EINPROGRESS) {
|
if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) {
|
||||||
timeout = internal_select(s, 1);
|
timeout = internal_select(s, 1);
|
||||||
res = connect(s->sock_fd, addr, addrlen);
|
res = connect(s->sock_fd, addr, addrlen);
|
||||||
if (res < 0 && errno == EISCONN)
|
if (res < 0 && errno == EISCONN)
|
||||||
@ -2084,6 +2098,9 @@ sock_recv(PySocketSockObject *s, PyObject *args)
|
|||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (!IS_SELECTABLE(s))
|
||||||
|
return select_error();
|
||||||
|
|
||||||
#ifndef __VMS
|
#ifndef __VMS
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
timeout = internal_select(s, 0);
|
timeout = internal_select(s, 0);
|
||||||
@ -2177,6 +2194,9 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args)
|
|||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (!IS_SELECTABLE(s))
|
||||||
|
return select_error();
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
memset(&addrbuf, 0, addrlen);
|
memset(&addrbuf, 0, addrlen);
|
||||||
timeout = internal_select(s, 0);
|
timeout = internal_select(s, 0);
|
||||||
@ -2238,6 +2258,9 @@ sock_send(PySocketSockObject *s, PyObject *args)
|
|||||||
if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
|
if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (!IS_SELECTABLE(s))
|
||||||
|
return select_error();
|
||||||
|
|
||||||
#ifndef __VMS
|
#ifndef __VMS
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
timeout = internal_select(s, 1);
|
timeout = internal_select(s, 1);
|
||||||
@ -2303,6 +2326,9 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
|
|||||||
if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
|
if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (!IS_SELECTABLE(s))
|
||||||
|
return select_error();
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
do {
|
do {
|
||||||
timeout = internal_select(s, 1);
|
timeout = internal_select(s, 1);
|
||||||
@ -2357,6 +2383,9 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
|
|||||||
if (!getsockaddrarg(s, addro, &addr, &addrlen))
|
if (!getsockaddrarg(s, addro, &addr, &addrlen))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (!IS_SELECTABLE(s))
|
||||||
|
return select_error();
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
timeout = internal_select(s, 1);
|
timeout = internal_select(s, 1);
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user