bpo-30058: Fixed buffer overflow in select.kqueue.control(). (#1095)

This commit is contained in:
Serhiy Storchaka 2017-10-12 22:17:46 +03:00 committed by GitHub
parent b7cbfe49e3
commit de07210077
3 changed files with 38 additions and 16 deletions

View file

@ -208,6 +208,30 @@ class TestKQueue(unittest.TestCase):
b.close() b.close()
kq.close() kq.close()
def test_issue30058(self):
# changelist must be an iterable
kq = select.kqueue()
a, b = socket.socketpair()
ev = select.kevent(a, select.KQ_FILTER_READ, select.KQ_EV_ADD | select.KQ_EV_ENABLE)
kq.control([ev], 0)
# not a list
kq.control((ev,), 0)
# __len__ is not consistent with __iter__
class BadList:
def __len__(self):
return 0
def __iter__(self):
for i in range(100):
yield ev
kq.control(BadList(), 0)
# doesn't have __len__
kq.control(iter([ev]), 0)
a.close()
b.close()
kq.close()
def test_close(self): def test_close(self):
open_file = open(__file__, "rb") open_file = open(__file__, "rb")
self.addCleanup(open_file.close) self.addCleanup(open_file.close)

View file

@ -0,0 +1 @@
Fixed buffer overflow in select.kqueue.control().

View file

@ -2102,7 +2102,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
int i = 0; int i = 0;
PyObject *otimeout = NULL; PyObject *otimeout = NULL;
PyObject *ch = NULL; PyObject *ch = NULL;
PyObject *it = NULL, *ei = NULL; PyObject *seq = NULL, *ei = NULL;
PyObject *result = NULL; PyObject *result = NULL;
struct kevent *evl = NULL; struct kevent *evl = NULL;
struct kevent *chl = NULL; struct kevent *chl = NULL;
@ -2148,37 +2148,34 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
} }
if (ch != NULL && ch != Py_None) { if (ch != NULL && ch != Py_None) {
it = PyObject_GetIter(ch); seq = PySequence_Fast(ch, "changelist is not iterable");
if (it == NULL) { if (seq == NULL) {
PyErr_SetString(PyExc_TypeError,
"changelist is not iterable");
return NULL; return NULL;
} }
nchanges = PyObject_Size(ch); if (PySequence_Fast_GET_SIZE(seq) > INT_MAX) {
if (nchanges < 0) { PyErr_SetString(PyExc_OverflowError,
"changelist is too long");
goto error; goto error;
} }
nchanges = (int)PySequence_Fast_GET_SIZE(seq);
chl = PyMem_New(struct kevent, nchanges); chl = PyMem_New(struct kevent, nchanges);
if (chl == NULL) { if (chl == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
goto error; goto error;
} }
i = 0; for (i = 0; i < nchanges; ++i) {
while ((ei = PyIter_Next(it)) != NULL) { ei = PySequence_Fast_GET_ITEM(seq, i);
if (!kqueue_event_Check(ei)) { if (!kqueue_event_Check(ei)) {
Py_DECREF(ei);
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"changelist must be an iterable of " "changelist must be an iterable of "
"select.kevent objects"); "select.kevent objects");
goto error; goto error;
} else {
chl[i++] = ((kqueue_event_Object *)ei)->e;
} }
Py_DECREF(ei); chl[i] = ((kqueue_event_Object *)ei)->e;
} }
Py_CLEAR(seq);
} }
Py_CLEAR(it);
/* event list */ /* event list */
if (nevents) { if (nevents) {
@ -2246,7 +2243,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
PyMem_Free(chl); PyMem_Free(chl);
PyMem_Free(evl); PyMem_Free(evl);
Py_XDECREF(result); Py_XDECREF(result);
Py_XDECREF(it); Py_XDECREF(seq);
return NULL; return NULL;
} }
@ -2254,7 +2251,7 @@ PyDoc_STRVAR(kqueue_queue_control_doc,
"control(changelist, max_events[, timeout=None]) -> eventlist\n\ "control(changelist, max_events[, timeout=None]) -> eventlist\n\
\n\ \n\
Calls the kernel kevent function.\n\ Calls the kernel kevent function.\n\
- changelist must be a list of kevent objects describing the changes\n\ - changelist must be an iterable of kevent objects describing the changes\n\
to be made to the kernel's watch list or None.\n\ to be made to the kernel's watch list or None.\n\
- max_events lets you specify the maximum number of events that the\n\ - max_events lets you specify the maximum number of events that the\n\
kernel will return.\n\ kernel will return.\n\