]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport:
authorNeal Norwitz <nnorwitz@gmail.com>
Tue, 7 Feb 2006 07:17:37 +0000 (07:17 +0000)
committerNeal Norwitz <nnorwitz@gmail.com>
Tue, 7 Feb 2006 07:17:37 +0000 (07:17 +0000)
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.

Misc/NEWS
Modules/_ssl.c
Modules/socketmodule.c

index 2b1cdbc097b711e1845e862a0a08f9d4b055f6e8..4d23a972c0f978194c1dc7e4e46d1db4dba64980 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -45,6 +45,9 @@ Core and builtins
 Extension Modules
 -----------------
 
+- Bug #876637, prevent stack corruption when socket descriptor
+  is larger than FD_SETSIZE.
+
 - Patch #1407135, bug #1424041: mmap.mmap(-1, size, ...) can return
   anonymous memory again on Unix.
 
index 9c100abfcdf7ea627316563cddee55ef5bb1fb3f..23e7538e7817deb4288853d1c9fdb49956f363a1 100644 (file)
@@ -74,6 +74,7 @@ typedef enum {
        SOCKET_IS_BLOCKING,
        SOCKET_HAS_TIMED_OUT,
        SOCKET_HAS_BEEN_CLOSED,
+       SOCKET_INVALID,
        SOCKET_OPERATION_OK
 } timeout_state;
 
@@ -272,6 +273,9 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file)
                } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
                        PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
                        goto fail;
+               } else if (sockstate == SOCKET_INVALID) {
+                       PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
+                       goto fail;
                } else if (sockstate == SOCKET_IS_NONBLOCKING) {
                        break;
                }
@@ -372,6 +376,10 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
        if (s->sock_fd < 0)
                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 */
        tv.tv_sec = (int)s->sock_timeout;
        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) {
                PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
                return NULL;
+       } else if (sockstate == SOCKET_INVALID) {
+               PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
+               return NULL;
        }
        do {
                err = 0;
@@ -467,6 +478,9 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
                PyErr_SetString(PySSLErrorObject, "The read operation timed out");
                Py_DECREF(buf);
                return NULL;
+       } else if (sockstate == SOCKET_INVALID) {
+               PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
+               return NULL;
        }
        do {
                err = 0;
index 4c0a0fcf5d5799932d784d6b532888ff11926c39..e0af01aa00c0ae38d00f623bcd5d4fd7e909943b 100644 (file)
@@ -390,6 +390,16 @@ static int taskwindow;
    there has to be a circular reference. */
 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
    and return a NULL pointer from a function. */
 
@@ -1362,6 +1372,9 @@ sock_accept(PySocketSockObject *s)
        newfd = -1;
 #endif
 
+       if (!IS_SELECTABLE(s))
+               return select_error();
+
        Py_BEGIN_ALLOW_THREADS
        timeout = internal_select(s, 0);
        if (!timeout)
@@ -1690,7 +1703,8 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
 #ifdef MS_WINDOWS
 
        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 */
                        fd_set fds;
                        fd_set fds_exc;
@@ -1735,7 +1749,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
 #else
 
        if (s->sock_timeout > 0.0) {
-               if (res < 0 && errno == EINPROGRESS) {
+               if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) {
                        timeout = internal_select(s, 1);
                        res = connect(s->sock_fd, addr, addrlen);
                        if (res < 0 && errno == EISCONN)
@@ -2038,6 +2052,9 @@ sock_recv(PySocketSockObject *s, PyObject *args)
        if (buf == NULL)
                return NULL;
 
+       if (!IS_SELECTABLE(s))
+               return select_error();
+
 #ifndef __VMS
        Py_BEGIN_ALLOW_THREADS
        timeout = internal_select(s, 0);
@@ -2131,6 +2148,9 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args)
        if (buf == NULL)
                return NULL;
 
+       if (!IS_SELECTABLE(s))
+               return select_error();
+
        Py_BEGIN_ALLOW_THREADS
        memset(&addrbuf, 0, addrlen);
        timeout = internal_select(s, 0);
@@ -2192,6 +2212,9 @@ sock_send(PySocketSockObject *s, PyObject *args)
        if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
                return NULL;
 
+       if (!IS_SELECTABLE(s))
+               return select_error();
+
 #ifndef __VMS
        Py_BEGIN_ALLOW_THREADS
        timeout = internal_select(s, 1);
@@ -2257,6 +2280,9 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
        if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
                return NULL;
 
+       if (!IS_SELECTABLE(s))
+               return select_error();
+
        Py_BEGIN_ALLOW_THREADS
        do {
                timeout = internal_select(s, 1);
@@ -2311,6 +2337,9 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
        if (!getsockaddrarg(s, addro, &addr, &addrlen))
                return NULL;
 
+       if (!IS_SELECTABLE(s))
+               return select_error();
+
        Py_BEGIN_ALLOW_THREADS
        timeout = internal_select(s, 1);
        if (!timeout)