From de072100775cc29e6cd93a75466cecbd1086f258 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 12 Oct 2017 22:17:46 +0300 Subject: bpo-30058: Fixed buffer overflow in select.kqueue.control(). (#1095) --- Lib/test/test_kqueue.py | 24 ++++++++++++++++++ .../2017-10-12-19-00-53.bpo-30058.cENtry.rst | 1 + Modules/selectmodule.c | 29 ++++++++++------------ 3 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-10-12-19-00-53.bpo-30058.cENtry.rst diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py index 9f49886..1099c75 100644 --- a/Lib/test/test_kqueue.py +++ b/Lib/test/test_kqueue.py @@ -208,6 +208,30 @@ class TestKQueue(unittest.TestCase): 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_close(self): open_file = open(__file__, "rb") self.addCleanup(open_file.close) 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 d763bae..66d1a37 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -2102,7 +2102,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args) 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; @@ -2148,37 +2148,34 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args) } 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) { @@ -2246,7 +2243,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args) PyMem_Free(chl); PyMem_Free(evl); Py_XDECREF(result); - Py_XDECREF(it); + Py_XDECREF(seq); return NULL; } @@ -2254,7 +2251,7 @@ PyDoc_STRVAR(kqueue_queue_control_doc, "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\ -- cgit v0.12