Commit 4857500a authored by Barry Warsaw's avatar Barry Warsaw

Reworked to eliminate all potential memory problems, including

deletion of object from list argument during callout to fileno().
parent 71f76e1f
...@@ -57,11 +57,26 @@ extern void bzero(); ...@@ -57,11 +57,26 @@ extern void bzero();
static PyObject *SelectError; static PyObject *SelectError;
typedef struct { /* list of Python objects and their file descriptor */ /* list of Python objects and their file descriptor */
PyObject *obj; typedef struct {
PyObject *obj; /* owned reference */
SOCKET fd; SOCKET fd;
int sentinel; /* -1 == sentinel */
} pylist; } pylist;
static void
reap_obj(fd2obj)
pylist fd2obj[FD_SETSIZE + 3];
{
int i;
for (i = 0; i < FD_SETSIZE + 3 && fd2obj[i].sentinel >= 0; i++) {
Py_XDECREF(fd2obj[i].obj);
fd2obj[i].obj = NULL;
}
fd2obj[0].sentinel = -1;
}
/* returns -1 and sets the Python exception if an error occurred, otherwise /* returns -1 and sets the Python exception if an error occurred, otherwise
returns a number >= 0 returns a number >= 0
*/ */
...@@ -71,67 +86,76 @@ list2set(list, set, fd2obj) ...@@ -71,67 +86,76 @@ list2set(list, set, fd2obj)
fd_set *set; fd_set *set;
pylist fd2obj[FD_SETSIZE + 3]; pylist fd2obj[FD_SETSIZE + 3];
{ {
int i, len, index, max = -1; int i;
PyObject *o, *fno; int max = -1;
PyObject *meth; int index = 0;
SOCKET v; int len = PyList_Size(list);
PyObject* o = NULL;
index = 0;
fd2obj[0].obj = (PyObject*)0; /* set list to zero size */ fd2obj[0].obj = (PyObject*)0; /* set list to zero size */
FD_ZERO(set); FD_ZERO(set);
len = PyList_Size(list);
for (i=0; i<len; i++) { for (i = 0; i < len; i++) {
PyObject *meth;
SOCKET v;
/* any intervening fileno() calls could decr this refcnt */
o = PyList_GetItem(list, i); o = PyList_GetItem(list, i);
Py_INCREF(o);
if (PyInt_Check(o)) { if (PyInt_Check(o)) {
v = PyInt_AsLong(o); v = PyInt_AsLong(o);
} }
else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL) else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL)
{ {
fno = PyEval_CallObject(meth, NULL); PyObject *fno = PyEval_CallObject(meth, NULL);
Py_DECREF(meth); Py_DECREF(meth);
if (fno == NULL) if (fno == NULL)
return -1; goto finally;
if (!PyInt_Check(fno)) { if (!PyInt_Check(fno)) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"fileno method returned a non-integer"); "fileno method returned a non-integer");
Py_DECREF(fno); Py_DECREF(fno);
return -1; goto finally;
} }
v = PyInt_AsLong(fno); v = PyInt_AsLong(fno);
Py_DECREF(fno); Py_DECREF(fno);
} }
else { else {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"argument must be an int, or have a fileno() method." "argument must be an int, or have a fileno() method.");
); goto finally;
return -1;
} }
#ifdef _MSC_VER #ifdef _MSC_VER
max = 0; /* not used for Win32 */ max = 0; /* not used for Win32 */
#else #else /* !_MSC_VER */
if (v < 0 || v >= FD_SETSIZE) { if (v < 0 || v >= FD_SETSIZE) {
PyErr_SetString( PyErr_SetString(PyExc_ValueError,
PyExc_ValueError, "filedescriptor out of range in select()");
"filedescriptor out of range in select()"); goto finally;
return -1;
} }
if (v > max) if (v > max)
max = v; max = v;
#endif #endif /* _MSC_VER */
FD_SET(v, set); FD_SET(v, set);
/* add object and its file descriptor to the list */ /* add object and its file descriptor to the list */
if (index >= FD_SETSIZE) { if (index >= FD_SETSIZE) {
PyErr_SetString( PyErr_SetString(PyExc_ValueError,
PyExc_ValueError, "too many file descriptors in select()");
"too many file descriptors in select()"); goto finally;
return -1;
} }
fd2obj[index].obj = o; fd2obj[index].obj = o;
fd2obj[index].fd = v; fd2obj[index].fd = v;
fd2obj[++index].obj = (PyObject *)0; /* sentinel */ fd2obj[index].sentinel = 0;
fd2obj[++index].sentinel = -1;
} }
return max+1; return max+1;
finally:
Py_XDECREF(o);
return -1;
} }
/* returns NULL and sets the Python exception if an error occurred */ /* returns NULL and sets the Python exception if an error occurred */
...@@ -140,40 +164,44 @@ set2list(set, fd2obj) ...@@ -140,40 +164,44 @@ set2list(set, fd2obj)
fd_set *set; fd_set *set;
pylist fd2obj[FD_SETSIZE + 3]; pylist fd2obj[FD_SETSIZE + 3];
{ {
int j, num=0; int i, j, count=0;
PyObject *list, *o; PyObject *list, *o;
SOCKET fd; SOCKET fd;
for (j=0; fd2obj[j].obj; j++) for (j = 0; fd2obj[j].sentinel >= 0; j++) {
if (FD_ISSET(fd2obj[j].fd, set)) if (FD_ISSET(fd2obj[j].fd, set))
num++; count++;
}
list = PyList_New(num); list = PyList_New(count);
if (!list) if (!list)
return NULL; return NULL;
num = 0; i = 0;
for (j=0; fd2obj[j].obj; j++) { for (j = 0; fd2obj[j].sentinel >= 0; j++) {
fd = fd2obj[j].fd; fd = fd2obj[j].fd;
if (FD_ISSET(fd, set)) { if (FD_ISSET(fd, set)) {
#ifndef _MSC_VER #ifndef _MSC_VER
if (fd > FD_SETSIZE) { if (fd > FD_SETSIZE) {
PyErr_SetString(PyExc_SystemError, PyErr_SetString(PyExc_SystemError,
"filedescriptor out of range returned in select()"); "filedescriptor out of range returned in select()");
return NULL; goto finally;
} }
#endif #endif
o = fd2obj[j].obj; o = fd2obj[j].obj;
Py_INCREF(o); fd2obj[j].obj = NULL;
if (PyList_SetItem(list, num, o) < 0) { /* transfer ownership */
Py_DECREF(list); if (PyList_SetItem(list, i, o) < 0)
return NULL; goto finally;
}
num++; i++;
} }
} }
return list; return list;
finally:
Py_DECREF(list);
return NULL;
} }
static PyObject * static PyObject *
select_select(self, args) select_select(self, args)
...@@ -184,7 +212,7 @@ select_select(self, args) ...@@ -184,7 +212,7 @@ select_select(self, args)
pylist wfd2obj[FD_SETSIZE + 3]; pylist wfd2obj[FD_SETSIZE + 3];
pylist efd2obj[FD_SETSIZE + 3]; pylist efd2obj[FD_SETSIZE + 3];
PyObject *ifdlist, *ofdlist, *efdlist; PyObject *ifdlist, *ofdlist, *efdlist;
PyObject *ret; PyObject *ret = NULL;
PyObject *tout = Py_None; PyObject *tout = Py_None;
fd_set ifdset, ofdset, efdset; fd_set ifdset, ofdset, efdset;
double timeout; double timeout;
...@@ -200,9 +228,11 @@ select_select(self, args) ...@@ -200,9 +228,11 @@ select_select(self, args)
if (tout == Py_None) if (tout == Py_None)
tvp = (struct timeval *)0; tvp = (struct timeval *)0;
else if (!PyArg_Parse(tout, "d;timeout must be float or None", else if (!PyArg_Parse(tout, "d", &timeout)) {
&timeout)) PyErr_SetString(PyExc_TypeError,
"timeout must be a float or None");
return NULL; return NULL;
}
else { else {
seconds = (int)timeout; seconds = (int)timeout;
timeout = timeout - (double)seconds; timeout = timeout - (double)seconds;
...@@ -224,12 +254,15 @@ select_select(self, args) ...@@ -224,12 +254,15 @@ select_select(self, args)
/* Convert lists to fd_sets, and get maximum fd number /* Convert lists to fd_sets, and get maximum fd number
* propagates the Python exception set in list2set() * propagates the Python exception set in list2set()
*/ */
rfd2obj[0].sentinel = -1;
wfd2obj[0].sentinel = -1;
efd2obj[0].sentinel = -1;
if ((imax=list2set(ifdlist, &ifdset, rfd2obj)) < 0) if ((imax=list2set(ifdlist, &ifdset, rfd2obj)) < 0)
return NULL; goto finally;
if ((omax=list2set(ofdlist, &ofdset, wfd2obj)) < 0) if ((omax=list2set(ofdlist, &ofdset, wfd2obj)) < 0)
return NULL; goto finally;
if ((emax=list2set(efdlist, &efdset, efd2obj)) < 0) if ((emax=list2set(efdlist, &efdset, efd2obj)) < 0)
return NULL; goto finally;
max = imax; max = imax;
if (omax > max) max = omax; if (omax > max) max = omax;
if (emax > max) max = emax; if (emax > max) max = emax;
...@@ -240,33 +273,37 @@ select_select(self, args) ...@@ -240,33 +273,37 @@ select_select(self, args)
if (n < 0) { if (n < 0) {
PyErr_SetFromErrno(SelectError); PyErr_SetFromErrno(SelectError);
return NULL;
} }
else if (n == 0) {
if (n == 0) { /* Speedup hack */ /* optimization */
ifdlist = PyList_New(0); ifdlist = PyList_New(0);
if (!ifdlist) if (ifdlist) {
return NULL; ret = Py_BuildValue("OOO", ifdlist, ifdlist, ifdlist);
ret = Py_BuildValue("OOO", ifdlist, ifdlist, ifdlist); Py_DECREF(ifdlist);
Py_XDECREF(ifdlist); }
return ret;
} }
else {
/* any of these three calls can raise an exception. it's more /* any of these three calls can raise an exception. it's more
convenient to test for this after all three calls... but is that convenient to test for this after all three calls... but
acceptable? is that acceptable?
*/ */
ifdlist = set2list(&ifdset, rfd2obj); ifdlist = set2list(&ifdset, rfd2obj);
ofdlist = set2list(&ofdset, wfd2obj); ofdlist = set2list(&ofdset, wfd2obj);
efdlist = set2list(&efdset, efd2obj); efdlist = set2list(&efdset, efd2obj);
if (PyErr_Occurred()) if (PyErr_Occurred())
ret = NULL; ret = NULL;
else else
ret = Py_BuildValue("OOO", ifdlist, ofdlist, efdlist); ret = Py_BuildValue("OOO", ifdlist, ofdlist, efdlist);
Py_DECREF(ifdlist); Py_DECREF(ifdlist);
Py_DECREF(ofdlist); Py_DECREF(ofdlist);
Py_DECREF(efdlist); Py_DECREF(efdlist);
}
finally:
reap_obj(rfd2obj);
reap_obj(wfd2obj);
reap_obj(efd2obj);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment