https://github.com/python/cpython/commit/c2eaeee3dc3306ca486b0377b07b1a957584b691
commit: c2eaeee3dc3306ca486b0377b07b1a957584b691
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
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 -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]