diff options
author | Neal Norwitz <nnorwitz@gmail.com> | 2006-02-07 07:17:37 (GMT) |
---|---|---|
committer | Neal Norwitz <nnorwitz@gmail.com> | 2006-02-07 07:17:37 (GMT) |
commit | 90f1db3d5da2947045653d7ecff43c4161061229 (patch) | |
tree | 3dd7d894f62b38d808a0221ada86a2760734a05b | |
parent | b5abd1615376bc7a66a16c5156a3a4cfc89eb54b (diff) | |
download | cpython-90f1db3d5da2947045653d7ecff43c4161061229.zip cpython-90f1db3d5da2947045653d7ecff43c4161061229.tar.gz cpython-90f1db3d5da2947045653d7ecff43c4161061229.tar.bz2 |
Backport:
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.
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_ssl.c | 14 | ||||
-rw-r--r-- | Modules/socketmodule.c | 33 |
3 files changed, 48 insertions, 2 deletions
@@ -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. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 9c100ab..23e7538 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -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; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 4c0a0fc..e0af01a 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -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) |