diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py index 643775597c56c67..f8e25b93a01a430 100644 --- a/Lib/test/test_selectors.py +++ b/Lib/test/test_selectors.py @@ -603,8 +603,7 @@ def test_empty_select_timeout(self): @unittest.skipUnless(hasattr(selectors, 'DevpollSelector'), "Test needs selectors.DevpollSelector") -class DevpollSelectorTestCase(BaseSelectorTestCase, ScalableSelectorMixIn, - unittest.TestCase): +class DevpollSelectorTestCase(BaseSelectorTestCase, unittest.TestCase): SELECTOR = getattr(selectors, 'DevpollSelector', None) diff --git a/Misc/NEWS.d/next/Library/2026-05-19-12-22-45.gh-issue-102494.Gae1Nl.rst b/Misc/NEWS.d/next/Library/2026-05-19-12-22-45.gh-issue-102494.Gae1Nl.rst new file mode 100644 index 000000000000000..9c3e8514172d01e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-19-12-22-45.gh-issue-102494.Gae1Nl.rst @@ -0,0 +1 @@ +Fixed excessive memory allocation and data race issues in Solaris specific ``DevPollSelector``. Patch by Jakub Kulik. diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index eb3148ef24631bb..e867e517f5b1f7a 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -824,12 +824,16 @@ poll_dealloc(PyObject *op) #ifdef HAVE_SYS_DEVPOLL_H static PyMethodDef devpoll_methods[]; +#define DEVPOLL_IN_BUFFER_SIZE 128 +#define DEVPOLL_MAX_OUT_BUFFER_SIZE 1024 + typedef struct { PyObject_HEAD int fd_devpoll; - int max_n_fds; int n_fds; + int out_size; struct pollfd *fds; + struct pollfd *out_fds; } devpollObject; #define devpollObject_CAST(op) ((devpollObject *)(op)) @@ -882,7 +886,7 @@ internal_devpoll_register(devpollObject *self, int fd, self->fds[self->n_fds].fd = fd; self->fds[self->n_fds].events = POLLREMOVE; - if (++self->n_fds == self->max_n_fds) { + if (++self->n_fds == DEVPOLL_IN_BUFFER_SIZE) { if (devpoll_flush(self)) return NULL; } @@ -891,7 +895,7 @@ internal_devpoll_register(devpollObject *self, int fd, self->fds[self->n_fds].fd = fd; self->fds[self->n_fds].events = (signed short)events; - if (++self->n_fds == self->max_n_fds) { + if (++self->n_fds == DEVPOLL_IN_BUFFER_SIZE) { if (devpoll_flush(self)) return NULL; } @@ -963,7 +967,7 @@ select_devpoll_unregister_impl(devpollObject *self, int fd) self->fds[self->n_fds].fd = fd; self->fds[self->n_fds].events = POLLREMOVE; - if (++self->n_fds == self->max_n_fds) { + if (++self->n_fds == DEVPOLL_IN_BUFFER_SIZE) { if (devpoll_flush(self)) return NULL; } @@ -1024,8 +1028,8 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj) if (devpoll_flush(self)) return NULL; - dvp.dp_fds = self->fds; - dvp.dp_nfds = self->max_n_fds; + dvp.dp_fds = self->out_fds; + dvp.dp_nfds = self->out_size; dvp.dp_timeout = (int)ms; if (timeout >= 0) { @@ -1069,8 +1073,8 @@ select_devpoll_poll_impl(devpollObject *self, PyObject *timeout_obj) return NULL; for (i = 0; i < poll_result; i++) { - num1 = PyLong_FromLong(self->fds[i].fd); - num2 = PyLong_FromLong(self->fds[i].revents); + num1 = PyLong_FromLong(self->out_fds[i].fd); + num2 = PyLong_FromLong(self->out_fds[i].revents); if ((num1 == NULL) || (num2 == NULL)) { Py_XDECREF(num1); Py_XDECREF(num2); @@ -1162,12 +1166,12 @@ static devpollObject * newDevPollObject(PyObject *module) { devpollObject *self; - int fd_devpoll, limit_result; - struct pollfd *fds; + int fd_devpoll, limit_result, out_size; + struct pollfd *fds, *out_fds; struct rlimit limit; /* - ** If we try to process more that getrlimit() + ** If we try to process more than getrlimit() ** fds, the kernel will give an error, so ** we set the limit here. It is a dynamic ** value, because we can change rlimit() anytime. @@ -1178,27 +1182,46 @@ newDevPollObject(PyObject *module) return NULL; } + /* + ** If the limit is too high (or RLIM_INFINITY), we might + ** allocate huge amounts of memory or even fail to allocate. + */ + out_size = limit.rlim_cur; + if ((rlim_t)out_size > DEVPOLL_MAX_OUT_BUFFER_SIZE) { + out_size = DEVPOLL_MAX_OUT_BUFFER_SIZE; + } + fd_devpoll = _Py_open("/dev/poll", O_RDWR); if (fd_devpoll == -1) return NULL; - fds = PyMem_NEW(struct pollfd, limit.rlim_cur); + fds = PyMem_NEW(struct pollfd, DEVPOLL_IN_BUFFER_SIZE); if (fds == NULL) { close(fd_devpoll); PyErr_NoMemory(); return NULL; } + out_fds = PyMem_NEW(struct pollfd, out_size); + if (fds == NULL) { + close(fd_devpoll); + PyMem_Free(fds); + PyErr_NoMemory(); + return NULL; + } + self = PyObject_New(devpollObject, get_select_state(module)->devpoll_Type); if (self == NULL) { close(fd_devpoll); PyMem_Free(fds); + PyMem_Free(out_fds); return NULL; } self->fd_devpoll = fd_devpoll; - self->max_n_fds = limit.rlim_cur; self->n_fds = 0; self->fds = fds; + self->out_size = out_size; + self->out_fds = out_fds; return self; } @@ -1210,6 +1233,7 @@ devpoll_dealloc(PyObject *op) PyTypeObject *type = Py_TYPE(self); (void)devpoll_internal_close(self); PyMem_Free(self->fds); + PyMem_Free(self->out_fds); PyObject_Free(self); Py_DECREF(type); }