[2.7] bpo-30058: Fixed buffer overflow in select.kqueue.control(). (GH-1095). (#3976)
(cherry picked from commit de072100775cc29e6cd93a75466cecbd1086f258)
diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py
index 1bdfec8..c599e02 100644
--- a/Lib/test/test_kqueue.py
+++ b/Lib/test/test_kqueue.py
@@ -205,6 +205,30 @@
b.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_main():
test_support.run_unittest(TestKQueue)
diff --git a/Misc/NEWS.d/next/Library/2017-10-12-19-00-53.bpo-30058.cENtry.rst b/Misc/NEWS.d/next/Library/2017-10-12-19-00-53.bpo-30058.cENtry.rst
new file mode 100644
index 0000000..fa1c8f4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2017-10-12-19-00-53.bpo-30058.cENtry.rst
@@ -0,0 +1 @@
+Fixed buffer overflow in select.kqueue.control().
diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c
index da03366..a38aa08 100644
--- a/Modules/selectmodule.c
+++ b/Modules/selectmodule.c
@@ -1537,7 +1537,7 @@
int i = 0;
PyObject *otimeout = NULL;
PyObject *ch = NULL;
- PyObject *it = NULL, *ei = NULL;
+ PyObject *seq = NULL, *ei = NULL;
PyObject *result = NULL;
struct kevent *evl = NULL;
struct kevent *chl = NULL;
@@ -1593,37 +1593,34 @@
}
if (ch != NULL && ch != Py_None) {
- it = PyObject_GetIter(ch);
- if (it == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "changelist is not iterable");
+ seq = PySequence_Fast(ch, "changelist is not iterable");
+ if (seq == NULL) {
return NULL;
}
- nchanges = PyObject_Size(ch);
- if (nchanges < 0) {
+ if (PySequence_Fast_GET_SIZE(seq) > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "changelist is too long");
goto error;
}
+ nchanges = (int)PySequence_Fast_GET_SIZE(seq);
chl = PyMem_New(struct kevent, nchanges);
if (chl == NULL) {
PyErr_NoMemory();
goto error;
}
- i = 0;
- while ((ei = PyIter_Next(it)) != NULL) {
+ for (i = 0; i < nchanges; ++i) {
+ ei = PySequence_Fast_GET_ITEM(seq, i);
if (!kqueue_event_Check(ei)) {
- Py_DECREF(ei);
PyErr_SetString(PyExc_TypeError,
"changelist must be an iterable of "
"select.kevent objects");
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 */
if (nevents) {
@@ -1667,7 +1664,7 @@
PyMem_Free(chl);
PyMem_Free(evl);
Py_XDECREF(result);
- Py_XDECREF(it);
+ Py_XDECREF(seq);
return NULL;
}
@@ -1675,7 +1672,7 @@
"control(changelist, max_events[, timeout=None]) -> eventlist\n\
\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\
- max_events lets you specify the maximum number of events that the\n\
kernel will return.\n\