Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:func:`fcntl.ioctl()` now accepts larger range of integers as the third
argument. On Linux signed and unsigned integers that fit in the size of a C
pointer are accepted. On other platforms integers that are representable as
C types ``int`` or ``unsigned int`` are accepted.
73 changes: 62 additions & 11 deletions Modules/fcntlmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,19 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code,
in their unsigned long ioctl codes this will break and need
special casing based on the platform being built on.
*/
int arg = 0;
int ret;
Py_buffer pstr;
char *str;
Py_ssize_t len;
char buf[IOCTL_BUFSZ+1]; /* argument plus NUL byte */

if (ob_arg != NULL) {
if (ob_arg == NULL) {
Py_BEGIN_ALLOW_THREADS
ret = ioctl(fd, code, NULL);
Py_END_ALLOW_THREADS
// Fall-through to outside the 'if' statement.
}
else {
if (PyArg_Parse(ob_arg, "w*:ioctl", &pstr)) {
char *arg;
str = pstr.buf;
Expand Down Expand Up @@ -246,17 +251,63 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code,
}

PyErr_Clear();
if (!PyArg_Parse(ob_arg,
"i;ioctl requires a file or file descriptor,"
" an integer and optionally an integer or buffer argument",
&arg)) {
return NULL;
/* Repeat the logic of PyArg_Parse(..., "i", ...), but for larger
integers. */
if (PyLong_Check(ob_arg)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess is that the i code allowed integer-compatible objects, while you are mandating an actual int here. This could be a regression. Perhaps use PyNumber_Index()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the i code is even more permissive (for example it accepts Decimal and Fraction). I have restored its logic for compatibility, but later I'm going to restrict the argument to index-like types in master.

Py_INCREF(ob_arg);
}
else {
if (PyFloat_Check(ob_arg)) {
ob_arg = NULL;
}
else {
ob_arg = (PyObject *)_PyLong_FromNbInt(ob_arg);
}
if (ob_arg == NULL) {
PyErr_SetString(PyExc_TypeError,
"ioctl requires a file or file descriptor,"
" an integer and optionally an integer or buffer argument");
return NULL;
}
}
{
assert(PyLong_Check(ob_arg));
#ifdef __linux__
char *arg;
arg = PyLong_AsVoidPtr(ob_arg);
Py_DECREF(ob_arg);
if (arg == NULL && PyErr_Occurred()) {
return NULL;
}
#else
unsigned int arg;
if (Py_SIZE(ob_arg) < 0) {
arg = (unsigned int)_PyLong_AsInt(ob_arg);
Py_DECREF(ob_arg);
if (arg == (unsigned int)-1 && PyErr_Occurred()) {
return NULL;
}
}
else {
unsigned long larg = PyLong_AsUnsignedLong(ob_arg);
Py_DECREF(ob_arg);
if ((larg == (unsigned long)-1 && PyErr_Occurred()) ||
(larg > UINT_MAX))
{
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C unsigned int");
return NULL;
}
arg = (unsigned int)larg;
}
#endif
Py_BEGIN_ALLOW_THREADS
ret = ioctl(fd, code, arg);
Py_END_ALLOW_THREADS
// Fall-through to outside the 'if' statement.
}
// Fall-through to outside the 'if' statement.
}
Py_BEGIN_ALLOW_THREADS
ret = ioctl(fd, code, arg);
Py_END_ALLOW_THREADS

if (ret < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
Expand Down