Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.13] gh-89928: Fix integer conversion of device numbers (GH-31794) #120053

Merged
merged 1 commit into from
Jun 4, 2024
Merged
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
15 changes: 13 additions & 2 deletions Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,21 +704,32 @@ def test_makedev(self):
self.assertEqual(posix.major(dev), major)
self.assertRaises(TypeError, posix.major, float(dev))
self.assertRaises(TypeError, posix.major)
self.assertRaises((ValueError, OverflowError), posix.major, -1)
for x in -2, 2**64, -2**63-1:
self.assertRaises((ValueError, OverflowError), posix.major, x)

minor = posix.minor(dev)
self.assertIsInstance(minor, int)
self.assertGreaterEqual(minor, 0)
self.assertEqual(posix.minor(dev), minor)
self.assertRaises(TypeError, posix.minor, float(dev))
self.assertRaises(TypeError, posix.minor)
self.assertRaises((ValueError, OverflowError), posix.minor, -1)
for x in -2, 2**64, -2**63-1:
self.assertRaises((ValueError, OverflowError), posix.minor, x)

self.assertEqual(posix.makedev(major, minor), dev)
self.assertRaises(TypeError, posix.makedev, float(major), minor)
self.assertRaises(TypeError, posix.makedev, major, float(minor))
self.assertRaises(TypeError, posix.makedev, major)
self.assertRaises(TypeError, posix.makedev)
for x in -2, 2**32, 2**64, -2**63-1:
self.assertRaises((ValueError, OverflowError), posix.makedev, x, minor)
self.assertRaises((ValueError, OverflowError), posix.makedev, major, x)

if sys.platform == 'linux':
NODEV = -1
self.assertEqual(posix.major(NODEV), NODEV)
self.assertEqual(posix.minor(NODEV), NODEV)
self.assertEqual(posix.makedev(NODEV, NODEV), NODEV)

def _test_all_chown_common(self, chown_func, first_param, stat_func):
"""Common code for chown, fchown and lchown tests."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix integer conversion in :func:`os.major`, :func:`os.minor`, and
:func:`os.makedev`. Support device numbers larger than ``2**63-1``. Support
non-existent device number (``NODEV``).
32 changes: 10 additions & 22 deletions Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

88 changes: 73 additions & 15 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "pycore_fileutils.h" // _Py_closerange()
#include "pycore_import.h" // _PyImport_ReInitLock()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
#include "pycore_long.h" // _PyLong_IsNegative()
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_object.h" // _PyObject_LookupSpecial()
#include "pycore_pylifecycle.h" // _PyOS_URandom()
Expand Down Expand Up @@ -967,16 +968,46 @@ _Py_Gid_Converter(PyObject *obj, gid_t *p)
#endif /* MS_WINDOWS */


#define _PyLong_FromDev PyLong_FromLongLong
static PyObject *
_PyLong_FromDev(dev_t dev)
{
#ifdef NODEV
if (dev == NODEV) {
return PyLong_FromLongLong((long long)dev);
}
#endif
return PyLong_FromUnsignedLongLong((unsigned long long)dev);
}


#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS)
static int
_Py_Dev_Converter(PyObject *obj, void *p)
{
*((dev_t *)p) = PyLong_AsUnsignedLongLong(obj);
if (PyErr_Occurred())
#ifdef NODEV
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
int overflow;
long long result = PyLong_AsLongLongAndOverflow(obj, &overflow);
if (result == -1 && PyErr_Occurred()) {
return 0;
}
if (!overflow && result == (long long)NODEV) {
*((dev_t *)p) = NODEV;
return 1;
}
}
#endif

unsigned long long result = PyLong_AsUnsignedLongLong(obj);
if (result == (unsigned long long)-1 && PyErr_Occurred()) {
return 0;
}
if ((unsigned long long)(dev_t)result != result) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C dev_t");
return 0;
}
*((dev_t *)p) = (dev_t)result;
return 1;
}
#endif /* (HAVE_MKNOD && HAVE_MAKEDEV) || HAVE_DEVICE_MACROS */
Expand Down Expand Up @@ -12517,55 +12548,82 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */


static PyObject *
major_minor_conv(unsigned int value)
{
#ifdef NODEV
if (value == (unsigned int)NODEV) {
return PyLong_FromLong((int)NODEV);
}
#endif
return PyLong_FromUnsignedLong(value);
}

static int
major_minor_check(dev_t value)
{
#ifdef NODEV
if (value == NODEV) {
return 1;
}
#endif
return (dev_t)(unsigned int)value == value;
}

#ifdef HAVE_DEVICE_MACROS
/*[clinic input]
os.major -> unsigned_int
os.major

device: dev_t
/

Extracts a device major number from a raw device number.
[clinic start generated code]*/

static unsigned int
static PyObject *
os_major_impl(PyObject *module, dev_t device)
/*[clinic end generated code: output=5b3b2589bafb498e input=1e16a4d30c4d4462]*/
/*[clinic end generated code: output=4071ffee17647891 input=b1a0a14ec9448229]*/
{
return major(device);
return major_minor_conv(major(device));
}


/*[clinic input]
os.minor -> unsigned_int
os.minor

device: dev_t
/

Extracts a device minor number from a raw device number.
[clinic start generated code]*/

static unsigned int
static PyObject *
os_minor_impl(PyObject *module, dev_t device)
/*[clinic end generated code: output=5e1a25e630b0157d input=0842c6d23f24c65e]*/
/*[clinic end generated code: output=306cb78e3bc5004f input=2f686e463682a9da]*/
{
return minor(device);
return major_minor_conv(minor(device));
}


/*[clinic input]
os.makedev -> dev_t

major: int
minor: int
major: dev_t
minor: dev_t
/

Composes a raw device number from the major and minor device numbers.
[clinic start generated code]*/

static dev_t
os_makedev_impl(PyObject *module, int major, int minor)
/*[clinic end generated code: output=881aaa4aba6f6a52 input=4b9fd8fc73cbe48f]*/
os_makedev_impl(PyObject *module, dev_t major, dev_t minor)
/*[clinic end generated code: output=cad6125c51f5af80 input=2146126ec02e55c1]*/
{
if (!major_minor_check(major) || !major_minor_check(minor)) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C unsigned int");
return (dev_t)-1;
}
return makedev(major, minor);
}
#endif /* HAVE_DEVICE_MACROS */
Expand Down
Loading