Skip to content

Commit de07210

Browse files
bpo-30058: Fixed buffer overflow in select.kqueue.control(). (#1095)
1 parent b7cbfe4 commit de07210

File tree

3 files changed

+38
-16
lines changed

3 files changed

+38
-16
lines changed

Lib/test/test_kqueue.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,30 @@ def testPair(self):
208208
b.close()
209209
kq.close()
210210

211+
def test_issue30058(self):
212+
# changelist must be an iterable
213+
kq = select.kqueue()
214+
a, b = socket.socketpair()
215+
ev = select.kevent(a, select.KQ_FILTER_READ, select.KQ_EV_ADD | select.KQ_EV_ENABLE)
216+
217+
kq.control([ev], 0)
218+
# not a list
219+
kq.control((ev,), 0)
220+
# __len__ is not consistent with __iter__
221+
class BadList:
222+
def __len__(self):
223+
return 0
224+
def __iter__(self):
225+
for i in range(100):
226+
yield ev
227+
kq.control(BadList(), 0)
228+
# doesn't have __len__
229+
kq.control(iter([ev]), 0)
230+
231+
a.close()
232+
b.close()
233+
kq.close()
234+
211235
def test_close(self):
212236
open_file = open(__file__, "rb")
213237
self.addCleanup(open_file.close)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed buffer overflow in select.kqueue.control().

Modules/selectmodule.c

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,7 +2102,7 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
21022102
int i = 0;
21032103
PyObject *otimeout = NULL;
21042104
PyObject *ch = NULL;
2105-
PyObject *it = NULL, *ei = NULL;
2105+
PyObject *seq = NULL, *ei = NULL;
21062106
PyObject *result = NULL;
21072107
struct kevent *evl = NULL;
21082108
struct kevent *chl = NULL;
@@ -2148,37 +2148,34 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
21482148
}
21492149

21502150
if (ch != NULL && ch != Py_None) {
2151-
it = PyObject_GetIter(ch);
2152-
if (it == NULL) {
2153-
PyErr_SetString(PyExc_TypeError,
2154-
"changelist is not iterable");
2151+
seq = PySequence_Fast(ch, "changelist is not iterable");
2152+
if (seq == NULL) {
21552153
return NULL;
21562154
}
2157-
nchanges = PyObject_Size(ch);
2158-
if (nchanges < 0) {
2155+
if (PySequence_Fast_GET_SIZE(seq) > INT_MAX) {
2156+
PyErr_SetString(PyExc_OverflowError,
2157+
"changelist is too long");
21592158
goto error;
21602159
}
2160+
nchanges = (int)PySequence_Fast_GET_SIZE(seq);
21612161

21622162
chl = PyMem_New(struct kevent, nchanges);
21632163
if (chl == NULL) {
21642164
PyErr_NoMemory();
21652165
goto error;
21662166
}
2167-
i = 0;
2168-
while ((ei = PyIter_Next(it)) != NULL) {
2167+
for (i = 0; i < nchanges; ++i) {
2168+
ei = PySequence_Fast_GET_ITEM(seq, i);
21692169
if (!kqueue_event_Check(ei)) {
2170-
Py_DECREF(ei);
21712170
PyErr_SetString(PyExc_TypeError,
21722171
"changelist must be an iterable of "
21732172
"select.kevent objects");
21742173
goto error;
2175-
} else {
2176-
chl[i++] = ((kqueue_event_Object *)ei)->e;
21772174
}
2178-
Py_DECREF(ei);
2175+
chl[i] = ((kqueue_event_Object *)ei)->e;
21792176
}
2177+
Py_CLEAR(seq);
21802178
}
2181-
Py_CLEAR(it);
21822179

21832180
/* event list */
21842181
if (nevents) {
@@ -2246,15 +2243,15 @@ kqueue_queue_control(kqueue_queue_Object *self, PyObject *args)
22462243
PyMem_Free(chl);
22472244
PyMem_Free(evl);
22482245
Py_XDECREF(result);
2249-
Py_XDECREF(it);
2246+
Py_XDECREF(seq);
22502247
return NULL;
22512248
}
22522249

22532250
PyDoc_STRVAR(kqueue_queue_control_doc,
22542251
"control(changelist, max_events[, timeout=None]) -> eventlist\n\
22552252
\n\
22562253
Calls the kernel kevent function.\n\
2257-
- changelist must be a list of kevent objects describing the changes\n\
2254+
- changelist must be an iterable of kevent objects describing the changes\n\
22582255
to be made to the kernel's watch list or None.\n\
22592256
- max_events lets you specify the maximum number of events that the\n\
22602257
kernel will return.\n\

0 commit comments

Comments
 (0)