From 202168498c37181242b4136dcfc9099185916287 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 20 Jun 2017 10:48:39 +0300 Subject: [PATCH 1/3] bpo-30665: fcntl.ioctl() now accepts larger range of integers as the third argument. On Linux signed and unsigned integers that fit in the size of C pointer are accepted. On other platforms integers that are representable as C types "int" or "unsigned int" are accepted. --- Misc/NEWS | 5 ++++ Modules/fcntlmodule.c | 55 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/Misc/NEWS b/Misc/NEWS index 0d1ed64cd62e26..0dedc33d8b7937 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -365,6 +365,11 @@ Extension Modules Library ------- +- bpo-30665: fcntl.ioctl() now accepts larger range of integers as the third + argument. On Linux signed and unsigned integers that fit in the size of + C pointer are accepted. On other platforms integers that are representable + as C types "int" or "unsigned int" are accepted. + - bpo-30038: Fix race condition between signal delivery and wakeup file descriptor. Patch by Nathaniel Smith. diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 0baaa83d2acae5..1f410a9a24e298 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -161,14 +161,18 @@ 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 + } + else { if (PyArg_Parse(ob_arg, "w*:ioctl", &pstr)) { char *arg; str = pstr.buf; @@ -246,17 +250,46 @@ 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; + if (PyLong_Check(ob_arg)) { +#ifdef __linux__ + char *arg; + arg = PyLong_AsVoidPtr(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); + if (arg == (unsigned int)-1 && PyErr_Occurred()) { + return NULL; + } + } + else { + unsigned long larg = PyLong_AsUnsignedLong(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. + } + else { + PyErr_SetString(PyExc_TypeError, + "ioctl requires a file or file descriptor," + " an integer and optionally an integer or buffer argument"); + return NULL; } - // 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; From 2b1294e16457b7853bb690e5f1eff30299d85c94 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 25 Jun 2017 16:46:44 +0300 Subject: [PATCH 2/3] Preserve backward compatibility. --- Modules/fcntlmodule.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index 1f410a9a24e298..df6fe446046b5e 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -171,6 +171,7 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, 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)) { @@ -250,10 +251,31 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, } PyErr_Clear(); + /* Repeat the logic of PyArg_Parse(..., "i", ...), but for larger + integers. */ if (PyLong_Check(ob_arg)) { + 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; } @@ -261,12 +283,14 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, 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)) { @@ -282,12 +306,6 @@ fcntl_ioctl_impl(PyObject *module, int fd, unsigned int code, Py_END_ALLOW_THREADS // Fall-through to outside the 'if' statement. } - else { - PyErr_SetString(PyExc_TypeError, - "ioctl requires a file or file descriptor," - " an integer and optionally an integer or buffer argument"); - return NULL; - } } if (ret < 0) { From 2a4be68309fc318cd279fe2dad5aa8dac4c2a569 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 8 Jun 2018 17:19:29 -0400 Subject: [PATCH 3/3] blurbify NEWS entry --- Misc/NEWS | 5 ----- .../next/Library/2018-06-08-17-19-03.bpo-30665.U04aet.rst | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-06-08-17-19-03.bpo-30665.U04aet.rst diff --git a/Misc/NEWS b/Misc/NEWS index 4d854479762b7d..83eb530602e03d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -374,11 +374,6 @@ Extension Modules Library ------- -- bpo-30665: fcntl.ioctl() now accepts larger range of integers as the third - argument. On Linux signed and unsigned integers that fit in the size of - C pointer are accepted. On other platforms integers that are representable - as C types "int" or "unsigned int" are accepted. - - bpo-30746: Prohibited the '=' character in environment variable names in ``os.putenv()`` and ``os.spawn*()``. diff --git a/Misc/NEWS.d/next/Library/2018-06-08-17-19-03.bpo-30665.U04aet.rst b/Misc/NEWS.d/next/Library/2018-06-08-17-19-03.bpo-30665.U04aet.rst new file mode 100644 index 00000000000000..af5c995e504587 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-06-08-17-19-03.bpo-30665.U04aet.rst @@ -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.