Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 082b2df

Browse files
committed
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.
1 parent 19cbcad commit 082b2df

3 files changed

Lines changed: 48 additions & 2 deletions

File tree

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ Core and builtins
216216
Extension Modules
217217
-----------------
218218

219+
- Bug #876637, prevent stack corruption when socket descriptor
220+
is larger than FD_SETSIZE.
221+
219222
- Patch #1407135, bug #1424041: harmonize mmap behavior of anonymous memory.
220223
mmap.mmap(-1, size) now returns anonymous memory in both Unix and Windows.
221224
mmap.mmap(0, size) should not be used on Windows for anonymous memory.

Modules/_ssl.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ typedef enum {
7474
SOCKET_IS_BLOCKING,
7575
SOCKET_HAS_TIMED_OUT,
7676
SOCKET_HAS_BEEN_CLOSED,
77+
SOCKET_INVALID,
7778
SOCKET_OPERATION_OK
7879
} timeout_state;
7980

@@ -272,6 +273,9 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file)
272273
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
273274
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
274275
goto fail;
276+
} else if (sockstate == SOCKET_INVALID) {
277+
PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
278+
goto fail;
275279
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
276280
break;
277281
}
@@ -372,6 +376,10 @@ check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing)
372376
if (s->sock_fd < 0)
373377
return SOCKET_HAS_BEEN_CLOSED;
374378

379+
/* Guard against socket too large for select*/
380+
if (s->sock_fd >= FD_SETSIZE)
381+
return SOCKET_INVALID;
382+
375383
/* Construct the arguments to select */
376384
tv.tv_sec = (int)s->sock_timeout;
377385
tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6);
@@ -409,6 +417,9 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args)
409417
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
410418
PyErr_SetString(PySSLErrorObject, "Underlying socket has been closed.");
411419
return NULL;
420+
} else if (sockstate == SOCKET_INVALID) {
421+
PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
422+
return NULL;
412423
}
413424
do {
414425
err = 0;
@@ -467,6 +478,9 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args)
467478
PyErr_SetString(PySSLErrorObject, "The read operation timed out");
468479
Py_DECREF(buf);
469480
return NULL;
481+
} else if (sockstate == SOCKET_INVALID) {
482+
PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select().");
483+
return NULL;
470484
}
471485
do {
472486
err = 0;

Modules/socketmodule.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,16 @@ static int taskwindow;
395395
there has to be a circular reference. */
396396
static PyTypeObject sock_type;
397397

398+
/* Can we call select() with this socket without a buffer overrun? */
399+
#define IS_SELECTABLE(s) ((s)->sock_fd < FD_SETSIZE)
400+
401+
static PyObject*
402+
select_error(void)
403+
{
404+
PyErr_SetString(socket_error, "unable to select on socket");
405+
return NULL;
406+
}
407+
398408
/* Convenience function to raise an error according to errno
399409
and return a NULL pointer from a function. */
400410

@@ -1408,6 +1418,9 @@ sock_accept(PySocketSockObject *s)
14081418
newfd = -1;
14091419
#endif
14101420

1421+
if (!IS_SELECTABLE(s))
1422+
return select_error();
1423+
14111424
Py_BEGIN_ALLOW_THREADS
14121425
timeout = internal_select(s, 0);
14131426
if (!timeout)
@@ -1736,7 +1749,8 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
17361749
#ifdef MS_WINDOWS
17371750

17381751
if (s->sock_timeout > 0.0) {
1739-
if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK) {
1752+
if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK &&
1753+
IS_SELECTABLE(s)) {
17401754
/* This is a mess. Best solution: trust select */
17411755
fd_set fds;
17421756
fd_set fds_exc;
@@ -1781,7 +1795,7 @@ internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen,
17811795
#else
17821796

17831797
if (s->sock_timeout > 0.0) {
1784-
if (res < 0 && errno == EINPROGRESS) {
1798+
if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) {
17851799
timeout = internal_select(s, 1);
17861800
res = connect(s->sock_fd, addr, addrlen);
17871801
if (res < 0 && errno == EISCONN)
@@ -2084,6 +2098,9 @@ sock_recv(PySocketSockObject *s, PyObject *args)
20842098
if (buf == NULL)
20852099
return NULL;
20862100

2101+
if (!IS_SELECTABLE(s))
2102+
return select_error();
2103+
20872104
#ifndef __VMS
20882105
Py_BEGIN_ALLOW_THREADS
20892106
timeout = internal_select(s, 0);
@@ -2177,6 +2194,9 @@ sock_recvfrom(PySocketSockObject *s, PyObject *args)
21772194
if (buf == NULL)
21782195
return NULL;
21792196

2197+
if (!IS_SELECTABLE(s))
2198+
return select_error();
2199+
21802200
Py_BEGIN_ALLOW_THREADS
21812201
memset(&addrbuf, 0, addrlen);
21822202
timeout = internal_select(s, 0);
@@ -2238,6 +2258,9 @@ sock_send(PySocketSockObject *s, PyObject *args)
22382258
if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
22392259
return NULL;
22402260

2261+
if (!IS_SELECTABLE(s))
2262+
return select_error();
2263+
22412264
#ifndef __VMS
22422265
Py_BEGIN_ALLOW_THREADS
22432266
timeout = internal_select(s, 1);
@@ -2303,6 +2326,9 @@ sock_sendall(PySocketSockObject *s, PyObject *args)
23032326
if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
23042327
return NULL;
23052328

2329+
if (!IS_SELECTABLE(s))
2330+
return select_error();
2331+
23062332
Py_BEGIN_ALLOW_THREADS
23072333
do {
23082334
timeout = internal_select(s, 1);
@@ -2357,6 +2383,9 @@ sock_sendto(PySocketSockObject *s, PyObject *args)
23572383
if (!getsockaddrarg(s, addro, &addr, &addrlen))
23582384
return NULL;
23592385

2386+
if (!IS_SELECTABLE(s))
2387+
return select_error();
2388+
23602389
Py_BEGIN_ALLOW_THREADS
23612390
timeout = internal_select(s, 1);
23622391
if (!timeout)

0 commit comments

Comments
 (0)