Skip to content

Commit c2eaeee

Browse files
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.
1 parent 0f84f6b commit c2eaeee

File tree

2 files changed

+40
-21
lines changed

2 files changed

+40
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`fcntl.fcntl` and :func:`fcntl.ioctl` can now detect a buffer overflow
2+
and raise :exc:`SystemError`. The stack and memory can be corrupted in such
3+
case, so treat this error as fatal.

Modules/fcntlmodule.c

+37-21
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
# include <stropts.h> // I_FLUSHBAND
2323
#endif
2424

25+
#define GUARDSZ 8
26+
// NUL followed by random bytes.
27+
static const char guard[GUARDSZ] = "\x00\xfa\x69\xc4\x67\xa3\x6c\x58";
28+
2529
/*[clinic input]
2630
module fcntl
2731
[clinic start generated code]*/
@@ -80,9 +84,10 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
8084
return PyLong_FromLong(ret);
8185
}
8286
if (PyUnicode_Check(arg) || PyObject_CheckBuffer(arg)) {
83-
#define FCNTL_BUFSZ 1024
8487
Py_buffer view;
85-
char buf[FCNTL_BUFSZ+1]; /* argument plus NUL byte */
88+
#define FCNTL_BUFSZ 1024
89+
/* argument plus NUL byte plus guard to detect a buffer overflow */
90+
char buf[FCNTL_BUFSZ+GUARDSZ];
8691

8792
if (!PyArg_Parse(arg, "s*", &view)) {
8893
return NULL;
@@ -95,7 +100,7 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
95100
return NULL;
96101
}
97102
memcpy(buf, view.buf, len);
98-
buf[len] = '\0';
103+
memcpy(buf + len, guard, GUARDSZ);
99104
PyBuffer_Release(&view);
100105

101106
do {
@@ -106,6 +111,10 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
106111
if (ret < 0) {
107112
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
108113
}
114+
if (memcmp(buf + len, guard, GUARDSZ) != 0) {
115+
PyErr_SetString(PyExc_SystemError, "buffer overflow");
116+
return NULL;
117+
}
109118
return PyBytes_FromStringAndSize(buf, len);
110119
#undef FCNTL_BUFSZ
111120
}
@@ -199,34 +208,37 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg,
199208
if (PyUnicode_Check(arg) || PyObject_CheckBuffer(arg)) {
200209
Py_buffer view;
201210
#define IOCTL_BUFSZ 1024
202-
char buf[IOCTL_BUFSZ+1]; /* argument plus NUL byte */
211+
/* argument plus NUL byte plus guard to detect a buffer overflow */
212+
char buf[IOCTL_BUFSZ+GUARDSZ];
203213
if (mutate_arg && !PyBytes_Check(arg) && !PyUnicode_Check(arg)) {
204214
if (PyObject_GetBuffer(arg, &view, PyBUF_WRITABLE) == 0) {
205-
if (view.len <= IOCTL_BUFSZ) {
206-
memcpy(buf, view.buf, view.len);
207-
buf[view.len] = '\0';
208-
do {
209-
Py_BEGIN_ALLOW_THREADS
210-
ret = ioctl(fd, code, buf);
211-
Py_END_ALLOW_THREADS
212-
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
213-
memcpy(view.buf, buf, view.len);
214-
}
215-
else {
216-
do {
217-
Py_BEGIN_ALLOW_THREADS
218-
ret = ioctl(fd, code, view.buf);
219-
Py_END_ALLOW_THREADS
220-
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
215+
Py_ssize_t len = view.len;
216+
void *ptr = view.buf;
217+
if (len <= IOCTL_BUFSZ) {
218+
memcpy(buf, ptr, len);
219+
memcpy(buf + len, guard, GUARDSZ);
220+
ptr = buf;
221221
}
222+
do {
223+
Py_BEGIN_ALLOW_THREADS
224+
ret = ioctl(fd, code, ptr);
225+
Py_END_ALLOW_THREADS
226+
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
222227
if (ret < 0) {
223228
if (!async_err) {
224229
PyErr_SetFromErrno(PyExc_OSError);
225230
}
226231
PyBuffer_Release(&view);
227232
return NULL;
228233
}
234+
if (ptr == buf) {
235+
memcpy(view.buf, buf, len);
236+
}
229237
PyBuffer_Release(&view);
238+
if (ptr == buf && memcmp(buf + len, guard, GUARDSZ) != 0) {
239+
PyErr_SetString(PyExc_SystemError, "buffer overflow");
240+
return NULL;
241+
}
230242
return PyLong_FromLong(ret);
231243
}
232244
if (!PyErr_ExceptionMatches(PyExc_BufferError)) {
@@ -246,7 +258,7 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg,
246258
return NULL;
247259
}
248260
memcpy(buf, view.buf, len);
249-
buf[len] = '\0';
261+
memcpy(buf + len, guard, GUARDSZ);
250262
PyBuffer_Release(&view);
251263

252264
do {
@@ -257,6 +269,10 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg,
257269
if (ret < 0) {
258270
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
259271
}
272+
if (memcmp(buf + len, guard, GUARDSZ) != 0) {
273+
PyErr_SetString(PyExc_SystemError, "buffer overflow");
274+
return NULL;
275+
}
260276
return PyBytes_FromStringAndSize(buf, len);
261277
#undef IOCTL_BUFSZ
262278
}

0 commit comments

Comments
 (0)