https://github.com/python/cpython/commit/c2eaeee3dc3306ca486b0377b07b1a957584b691 commit: c2eaeee3dc3306ca486b0377b07b1a957584b691 branch: main author: Serhiy Storchaka <storch...@gmail.com> committer: serhiy-storchaka <storch...@gmail.com> date: 2025-04-28T17:56:10+03:00 summary:
gh-132915: Try to detect a buffer overflow in fcntl() and ioctl() (GH-132919) SystemError is raised when buffer overflow is detected. The stack and memory can already be corrupted, so treat this error as fatal. files: A Misc/NEWS.d/next/Library/2025-04-25-12-55-06.gh-issue-132915.XuKCXn.rst M Modules/fcntlmodule.c diff --git a/Misc/NEWS.d/next/Library/2025-04-25-12-55-06.gh-issue-132915.XuKCXn.rst b/Misc/NEWS.d/next/Library/2025-04-25-12-55-06.gh-issue-132915.XuKCXn.rst new file mode 100644 index 00000000000000..95a7d9e9159d59 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-25-12-55-06.gh-issue-132915.XuKCXn.rst @@ -0,0 +1,3 @@ +:func:`fcntl.fcntl` and :func:`fcntl.ioctl` can now detect a buffer overflow +and raise :exc:`SystemError`. The stack and memory can be corrupted in such +case, so treat this error as fatal. diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 7e14e6bc3a525d..ebcacd2fb0ece1 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -22,6 +22,10 @@ # include <stropts.h> // I_FLUSHBAND #endif +#define GUARDSZ 8 +// NUL followed by random bytes. +static const char guard[GUARDSZ] = "\x00\xfa\x69\xc4\x67\xa3\x6c\x58"; + /*[clinic input] module fcntl [clinic start generated code]*/ @@ -80,9 +84,10 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) return PyLong_FromLong(ret); } if (PyUnicode_Check(arg) || PyObject_CheckBuffer(arg)) { -#define FCNTL_BUFSZ 1024 Py_buffer view; - char buf[FCNTL_BUFSZ+1]; /* argument plus NUL byte */ +#define FCNTL_BUFSZ 1024 + /* argument plus NUL byte plus guard to detect a buffer overflow */ + char buf[FCNTL_BUFSZ+GUARDSZ]; if (!PyArg_Parse(arg, "s*", &view)) { return NULL; @@ -95,7 +100,7 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) return NULL; } memcpy(buf, view.buf, len); - buf[len] = '\0'; + memcpy(buf + len, guard, GUARDSZ); PyBuffer_Release(&view); do { @@ -106,6 +111,10 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) if (ret < 0) { return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL; } + if (memcmp(buf + len, guard, GUARDSZ) != 0) { + PyErr_SetString(PyExc_SystemError, "buffer overflow"); + return NULL; + } return PyBytes_FromStringAndSize(buf, len); #undef FCNTL_BUFSZ } @@ -199,26 +208,22 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, if (PyUnicode_Check(arg) || PyObject_CheckBuffer(arg)) { Py_buffer view; #define IOCTL_BUFSZ 1024 - char buf[IOCTL_BUFSZ+1]; /* argument plus NUL byte */ + /* argument plus NUL byte plus guard to detect a buffer overflow */ + char buf[IOCTL_BUFSZ+GUARDSZ]; if (mutate_arg && !PyBytes_Check(arg) && !PyUnicode_Check(arg)) { if (PyObject_GetBuffer(arg, &view, PyBUF_WRITABLE) == 0) { - if (view.len <= IOCTL_BUFSZ) { - memcpy(buf, view.buf, view.len); - buf[view.len] = '\0'; - do { - Py_BEGIN_ALLOW_THREADS - ret = ioctl(fd, code, buf); - Py_END_ALLOW_THREADS - } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - memcpy(view.buf, buf, view.len); - } - else { - do { - Py_BEGIN_ALLOW_THREADS - ret = ioctl(fd, code, view.buf); - Py_END_ALLOW_THREADS - } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + Py_ssize_t len = view.len; + void *ptr = view.buf; + if (len <= IOCTL_BUFSZ) { + memcpy(buf, ptr, len); + memcpy(buf + len, guard, GUARDSZ); + ptr = buf; } + do { + Py_BEGIN_ALLOW_THREADS + ret = ioctl(fd, code, ptr); + Py_END_ALLOW_THREADS + } while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals())); if (ret < 0) { if (!async_err) { PyErr_SetFromErrno(PyExc_OSError); @@ -226,7 +231,14 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, PyBuffer_Release(&view); return NULL; } + if (ptr == buf) { + memcpy(view.buf, buf, len); + } PyBuffer_Release(&view); + if (ptr == buf && memcmp(buf + len, guard, GUARDSZ) != 0) { + PyErr_SetString(PyExc_SystemError, "buffer overflow"); + return NULL; + } return PyLong_FromLong(ret); } if (!PyErr_ExceptionMatches(PyExc_BufferError)) { @@ -246,7 +258,7 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, return NULL; } memcpy(buf, view.buf, len); - buf[len] = '\0'; + memcpy(buf + len, guard, GUARDSZ); PyBuffer_Release(&view); do { @@ -257,6 +269,10 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg, if (ret < 0) { return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL; } + if (memcmp(buf + len, guard, GUARDSZ) != 0) { + PyErr_SetString(PyExc_SystemError, "buffer overflow"); + return NULL; + } return PyBytes_FromStringAndSize(buf, len); #undef IOCTL_BUFSZ } _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com