mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-95380: Remove the 1024 bytes limit in fcntl.fcntl() and fcntl.ioctl() (GH-132907)
This commit is contained in:
parent
fe9f6e829a
commit
18bf8f84aa
5 changed files with 147 additions and 46 deletions
|
@ -107,15 +107,15 @@ The module defines the following functions:
|
||||||
passed to the C :c:func:`fcntl` call. The return value after a successful
|
passed to the C :c:func:`fcntl` call. The return value after a successful
|
||||||
call is the contents of the buffer, converted to a :class:`bytes` object.
|
call is the contents of the buffer, converted to a :class:`bytes` object.
|
||||||
The length of the returned object will be the same as the length of the
|
The length of the returned object will be the same as the length of the
|
||||||
*arg* argument. This is limited to 1024 bytes.
|
*arg* argument.
|
||||||
|
|
||||||
If the :c:func:`fcntl` call fails, an :exc:`OSError` is raised.
|
If the :c:func:`fcntl` call fails, an :exc:`OSError` is raised.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If the type or the size of *arg* does not match the type or size
|
If the type or size of *arg* does not match the type or size
|
||||||
of the argument of the operation (for example, if an integer is
|
of the operation's argument (for example, if an integer is
|
||||||
passed when a pointer is expected, or the information returned in
|
passed when a pointer is expected, or the information returned in
|
||||||
the buffer by the operating system is larger than 1024 bytes),
|
the buffer by the operating system is larger than the size of *arg*),
|
||||||
this is most likely to result in a segmentation violation or
|
this is most likely to result in a segmentation violation or
|
||||||
a more subtle data corruption.
|
a more subtle data corruption.
|
||||||
|
|
||||||
|
@ -125,6 +125,9 @@ The module defines the following functions:
|
||||||
Add support of arbitrary :term:`bytes-like objects <bytes-like object>`,
|
Add support of arbitrary :term:`bytes-like objects <bytes-like object>`,
|
||||||
not only :class:`bytes`.
|
not only :class:`bytes`.
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
The size of bytes-like objects is no longer limited to 1024 bytes.
|
||||||
|
|
||||||
|
|
||||||
.. function:: ioctl(fd, request, arg=0, mutate_flag=True, /)
|
.. function:: ioctl(fd, request, arg=0, mutate_flag=True, /)
|
||||||
|
|
||||||
|
@ -161,8 +164,7 @@ The module defines the following functions:
|
||||||
If the type or size of *arg* does not match the type or size
|
If the type or size of *arg* does not match the type or size
|
||||||
of the operation's argument (for example, if an integer is
|
of the operation's argument (for example, if an integer is
|
||||||
passed when a pointer is expected, or the information returned in
|
passed when a pointer is expected, or the information returned in
|
||||||
the buffer by the operating system is larger than 1024 bytes,
|
the buffer by the operating system is larger than the size of *arg*),
|
||||||
or the size of the mutable bytes-like object is too small),
|
|
||||||
this is most likely to result in a segmentation violation or
|
this is most likely to result in a segmentation violation or
|
||||||
a more subtle data corruption.
|
a more subtle data corruption.
|
||||||
|
|
||||||
|
@ -185,6 +187,10 @@ The module defines the following functions:
|
||||||
The GIL is always released during a system call.
|
The GIL is always released during a system call.
|
||||||
System calls failing with EINTR are automatically retried.
|
System calls failing with EINTR are automatically retried.
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
The size of not mutated bytes-like objects is no longer
|
||||||
|
limited to 1024 bytes.
|
||||||
|
|
||||||
.. function:: flock(fd, operation, /)
|
.. function:: flock(fd, operation, /)
|
||||||
|
|
||||||
Perform the lock operation *operation* on file descriptor *fd* (file objects providing
|
Perform the lock operation *operation* on file descriptor *fd* (file objects providing
|
||||||
|
|
|
@ -228,6 +228,52 @@ class TestFcntl(unittest.TestCase):
|
||||||
os.close(test_pipe_r)
|
os.close(test_pipe_r)
|
||||||
os.close(test_pipe_w)
|
os.close(test_pipe_w)
|
||||||
|
|
||||||
|
def _check_fcntl_not_mutate_len(self, nbytes=None):
|
||||||
|
self.f = open(TESTFN, 'wb')
|
||||||
|
buf = struct.pack('ii', fcntl.F_OWNER_PID, os.getpid())
|
||||||
|
if nbytes is not None:
|
||||||
|
buf += b' ' * (nbytes - len(buf))
|
||||||
|
else:
|
||||||
|
nbytes = len(buf)
|
||||||
|
save_buf = bytes(buf)
|
||||||
|
r = fcntl.fcntl(self.f, fcntl.F_SETOWN_EX, buf)
|
||||||
|
self.assertIsInstance(r, bytes)
|
||||||
|
self.assertEqual(len(r), len(save_buf))
|
||||||
|
self.assertEqual(buf, save_buf)
|
||||||
|
type, pid = memoryview(r).cast('i')[:2]
|
||||||
|
self.assertEqual(type, fcntl.F_OWNER_PID)
|
||||||
|
self.assertEqual(pid, os.getpid())
|
||||||
|
|
||||||
|
buf = b' ' * nbytes
|
||||||
|
r = fcntl.fcntl(self.f, fcntl.F_GETOWN_EX, buf)
|
||||||
|
self.assertIsInstance(r, bytes)
|
||||||
|
self.assertEqual(len(r), len(save_buf))
|
||||||
|
self.assertEqual(buf, b' ' * nbytes)
|
||||||
|
type, pid = memoryview(r).cast('i')[:2]
|
||||||
|
self.assertEqual(type, fcntl.F_OWNER_PID)
|
||||||
|
self.assertEqual(pid, os.getpid())
|
||||||
|
|
||||||
|
buf = memoryview(b' ' * nbytes)
|
||||||
|
r = fcntl.fcntl(self.f, fcntl.F_GETOWN_EX, buf)
|
||||||
|
self.assertIsInstance(r, bytes)
|
||||||
|
self.assertEqual(len(r), len(save_buf))
|
||||||
|
self.assertEqual(bytes(buf), b' ' * nbytes)
|
||||||
|
type, pid = memoryview(r).cast('i')[:2]
|
||||||
|
self.assertEqual(type, fcntl.F_OWNER_PID)
|
||||||
|
self.assertEqual(pid, os.getpid())
|
||||||
|
|
||||||
|
@unittest.skipUnless(
|
||||||
|
hasattr(fcntl, "F_SETOWN_EX") and hasattr(fcntl, "F_GETOWN_EX"),
|
||||||
|
"requires F_SETOWN_EX and F_GETOWN_EX")
|
||||||
|
def test_fcntl_small_buffer(self):
|
||||||
|
self._check_fcntl_not_mutate_len()
|
||||||
|
|
||||||
|
@unittest.skipUnless(
|
||||||
|
hasattr(fcntl, "F_SETOWN_EX") and hasattr(fcntl, "F_GETOWN_EX"),
|
||||||
|
"requires F_SETOWN_EX and F_GETOWN_EX")
|
||||||
|
def test_fcntl_large_buffer(self):
|
||||||
|
self._check_fcntl_not_mutate_len(2024)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -127,9 +127,8 @@ class IoctlTestsTty(unittest.TestCase):
|
||||||
self._check_ioctl_not_mutate_len(1024)
|
self._check_ioctl_not_mutate_len(1024)
|
||||||
|
|
||||||
def test_ioctl_mutate_2048(self):
|
def test_ioctl_mutate_2048(self):
|
||||||
# Test with a larger buffer, just for the record.
|
|
||||||
self._check_ioctl_mutate_len(2048)
|
self._check_ioctl_mutate_len(2048)
|
||||||
self.assertRaises(ValueError, self._check_ioctl_not_mutate_len, 2048)
|
self._check_ioctl_not_mutate_len(1024)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()")
|
@unittest.skipUnless(hasattr(os, 'openpty'), "need os.openpty()")
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:func:`fcntl.fcntl` and :func:`fcntl.ioctl`: Remove the 1024 bytes limit
|
||||||
|
on the size of not mutated bytes-like argument.
|
|
@ -93,29 +93,53 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_ssize_t len = view.len;
|
Py_ssize_t len = view.len;
|
||||||
if (len > FCNTL_BUFSZ) {
|
if (len <= FCNTL_BUFSZ) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
memcpy(buf, view.buf, len);
|
||||||
"fcntl argument 3 is too long");
|
memcpy(buf + len, guard, GUARDSZ);
|
||||||
PyBuffer_Release(&view);
|
PyBuffer_Release(&view);
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memcpy(buf, view.buf, len);
|
|
||||||
memcpy(buf + len, guard, GUARDSZ);
|
|
||||||
PyBuffer_Release(&view);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
ret = fcntl(fd, code, buf);
|
ret = fcntl(fd, code, buf);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
|
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
|
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);
|
||||||
}
|
}
|
||||||
if (memcmp(buf + len, guard, GUARDSZ) != 0) {
|
else {
|
||||||
PyErr_SetString(PyExc_SystemError, "buffer overflow");
|
PyObject *result = PyBytes_FromStringAndSize(NULL, len);
|
||||||
return NULL;
|
if (result == NULL) {
|
||||||
|
PyBuffer_Release(&view);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *ptr = PyBytes_AsString(result);
|
||||||
|
memcpy(ptr, view.buf, len);
|
||||||
|
PyBuffer_Release(&view);
|
||||||
|
|
||||||
|
do {
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
ret = fcntl(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);
|
||||||
|
}
|
||||||
|
Py_DECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (ptr[len] != '\0') {
|
||||||
|
PyErr_SetString(PyExc_SystemError, "buffer overflow");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return PyBytes_FromStringAndSize(buf, len);
|
|
||||||
#undef FCNTL_BUFSZ
|
#undef FCNTL_BUFSZ
|
||||||
}
|
}
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
@ -251,29 +275,53 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned long code, PyObject *arg,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Py_ssize_t len = view.len;
|
Py_ssize_t len = view.len;
|
||||||
if (len > IOCTL_BUFSZ) {
|
if (len <= IOCTL_BUFSZ) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
memcpy(buf, view.buf, len);
|
||||||
"ioctl argument 3 is too long");
|
memcpy(buf + len, guard, GUARDSZ);
|
||||||
PyBuffer_Release(&view);
|
PyBuffer_Release(&view);
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memcpy(buf, view.buf, len);
|
|
||||||
memcpy(buf + len, guard, GUARDSZ);
|
|
||||||
PyBuffer_Release(&view);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
ret = ioctl(fd, code, buf);
|
ret = ioctl(fd, code, buf);
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
|
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
|
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);
|
||||||
}
|
}
|
||||||
if (memcmp(buf + len, guard, GUARDSZ) != 0) {
|
else {
|
||||||
PyErr_SetString(PyExc_SystemError, "buffer overflow");
|
PyObject *result = PyBytes_FromStringAndSize(NULL, len);
|
||||||
return NULL;
|
if (result == NULL) {
|
||||||
|
PyBuffer_Release(&view);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *ptr = PyBytes_AsString(result);
|
||||||
|
memcpy(ptr, view.buf, len);
|
||||||
|
PyBuffer_Release(&view);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
Py_DECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (ptr[len] != '\0') {
|
||||||
|
PyErr_SetString(PyExc_SystemError, "buffer overflow");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return PyBytes_FromStringAndSize(buf, len);
|
|
||||||
#undef IOCTL_BUFSZ
|
#undef IOCTL_BUFSZ
|
||||||
}
|
}
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue