Skip to content

Commit

Permalink
pythongh-89928: Fix integer conversion of device numbers (pythonGH-31794
Browse files Browse the repository at this point in the history
)

Fix os.major(), os.minor() and os.makedev().
Support device numbers larger than 2**63-1.
Support non-existent device number (NODEV).
  • Loading branch information
serhiy-storchaka authored Jun 4, 2024
1 parent 5f03f09 commit 7111d96
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 39 deletions.
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

0 comments on commit 7111d96

Please sign in to comment.