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.
This commit is contained in:
Serhiy Storchaka 2025-04-28 17:56:10 +03:00 committed by GitHub
parent 0f84f6b334
commit c2eaeee3dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 21 deletions

View file

@ -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.

View file

@ -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
}