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:
Neal Norwitz 2006-02-07 07:04:46 +00:00
parent 19cbcad20e
commit 082b2df33f
3 changed files with 48 additions and 2 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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)