Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Lib/test/test_kqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,30 @@ def testPair(self):
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)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed buffer overflow in select.kqueue.control().
29 changes: 13 additions & 16 deletions Modules/selectmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1537,7 +1537,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;
Expand Down Expand Up @@ -1593,37 +1593,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) {
Expand Down Expand Up @@ -1667,15 +1664,15 @@ 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;
}

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\
Expand Down