Skip to content

Commit 2acbdc2

Browse files
[3.13] gh-89928: Fix integer conversion of device numbers (GH-31794) (GH-120053)
Fix os.major(), os.minor() and os.makedev(). Support device numbers larger than 2**63-1. Support non-existent device number (NODEV). (cherry picked from commit 7111d96) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 54023a6 commit 2acbdc2

File tree

4 files changed

+99
-39
lines changed

4 files changed

+99
-39
lines changed

Lib/test/test_posix.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,21 +704,32 @@ def test_makedev(self):
704704
self.assertEqual(posix.major(dev), major)
705705
self.assertRaises(TypeError, posix.major, float(dev))
706706
self.assertRaises(TypeError, posix.major)
707-
self.assertRaises((ValueError, OverflowError), posix.major, -1)
707+
for x in -2, 2**64, -2**63-1:
708+
self.assertRaises((ValueError, OverflowError), posix.major, x)
708709

709710
minor = posix.minor(dev)
710711
self.assertIsInstance(minor, int)
711712
self.assertGreaterEqual(minor, 0)
712713
self.assertEqual(posix.minor(dev), minor)
713714
self.assertRaises(TypeError, posix.minor, float(dev))
714715
self.assertRaises(TypeError, posix.minor)
715-
self.assertRaises((ValueError, OverflowError), posix.minor, -1)
716+
for x in -2, 2**64, -2**63-1:
717+
self.assertRaises((ValueError, OverflowError), posix.minor, x)
716718

717719
self.assertEqual(posix.makedev(major, minor), dev)
718720
self.assertRaises(TypeError, posix.makedev, float(major), minor)
719721
self.assertRaises(TypeError, posix.makedev, major, float(minor))
720722
self.assertRaises(TypeError, posix.makedev, major)
721723
self.assertRaises(TypeError, posix.makedev)
724+
for x in -2, 2**32, 2**64, -2**63-1:
725+
self.assertRaises((ValueError, OverflowError), posix.makedev, x, minor)
726+
self.assertRaises((ValueError, OverflowError), posix.makedev, major, x)
727+
728+
if sys.platform == 'linux':
729+
NODEV = -1
730+
self.assertEqual(posix.major(NODEV), NODEV)
731+
self.assertEqual(posix.minor(NODEV), NODEV)
732+
self.assertEqual(posix.makedev(NODEV, NODEV), NODEV)
722733

723734
def _test_all_chown_common(self, chown_func, first_param, stat_func):
724735
"""Common code for chown, fchown and lchown tests."""
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix integer conversion in :func:`os.major`, :func:`os.minor`, and
2+
:func:`os.makedev`. Support device numbers larger than ``2**63-1``. Support
3+
non-existent device number (``NODEV``).

Modules/clinic/posixmodule.c.h

Lines changed: 10 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "pycore_fileutils.h" // _Py_closerange()
1919
#include "pycore_import.h" // _PyImport_ReInitLock()
2020
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
21+
#include "pycore_long.h" // _PyLong_IsNegative()
2122
#include "pycore_moduleobject.h" // _PyModule_GetState()
2223
#include "pycore_object.h" // _PyObject_LookupSpecial()
2324
#include "pycore_pylifecycle.h" // _PyOS_URandom()
@@ -967,16 +968,46 @@ _Py_Gid_Converter(PyObject *obj, gid_t *p)
967968
#endif /* MS_WINDOWS */
968969

969970

970-
#define _PyLong_FromDev PyLong_FromLongLong
971+
static PyObject *
972+
_PyLong_FromDev(dev_t dev)
973+
{
974+
#ifdef NODEV
975+
if (dev == NODEV) {
976+
return PyLong_FromLongLong((long long)dev);
977+
}
978+
#endif
979+
return PyLong_FromUnsignedLongLong((unsigned long long)dev);
980+
}
971981

972982

973983
#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS)
974984
static int
975985
_Py_Dev_Converter(PyObject *obj, void *p)
976986
{
977-
*((dev_t *)p) = PyLong_AsUnsignedLongLong(obj);
978-
if (PyErr_Occurred())
987+
#ifdef NODEV
988+
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
989+
int overflow;
990+
long long result = PyLong_AsLongLongAndOverflow(obj, &overflow);
991+
if (result == -1 && PyErr_Occurred()) {
992+
return 0;
993+
}
994+
if (!overflow && result == (long long)NODEV) {
995+
*((dev_t *)p) = NODEV;
996+
return 1;
997+
}
998+
}
999+
#endif
1000+
1001+
unsigned long long result = PyLong_AsUnsignedLongLong(obj);
1002+
if (result == (unsigned long long)-1 && PyErr_Occurred()) {
1003+
return 0;
1004+
}
1005+
if ((unsigned long long)(dev_t)result != result) {
1006+
PyErr_SetString(PyExc_OverflowError,
1007+
"Python int too large to convert to C dev_t");
9791008
return 0;
1009+
}
1010+
*((dev_t *)p) = (dev_t)result;
9801011
return 1;
9811012
}
9821013
#endif /* (HAVE_MKNOD && HAVE_MAKEDEV) || HAVE_DEVICE_MACROS */
@@ -12517,55 +12548,82 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
1251712548
#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */
1251812549

1251912550

12551+
static PyObject *
12552+
major_minor_conv(unsigned int value)
12553+
{
12554+
#ifdef NODEV
12555+
if (value == (unsigned int)NODEV) {
12556+
return PyLong_FromLong((int)NODEV);
12557+
}
12558+
#endif
12559+
return PyLong_FromUnsignedLong(value);
12560+
}
12561+
12562+
static int
12563+
major_minor_check(dev_t value)
12564+
{
12565+
#ifdef NODEV
12566+
if (value == NODEV) {
12567+
return 1;
12568+
}
12569+
#endif
12570+
return (dev_t)(unsigned int)value == value;
12571+
}
12572+
1252012573
#ifdef HAVE_DEVICE_MACROS
1252112574
/*[clinic input]
12522-
os.major -> unsigned_int
12575+
os.major
1252312576
1252412577
device: dev_t
1252512578
/
1252612579
1252712580
Extracts a device major number from a raw device number.
1252812581
[clinic start generated code]*/
1252912582

12530-
static unsigned int
12583+
static PyObject *
1253112584
os_major_impl(PyObject *module, dev_t device)
12532-
/*[clinic end generated code: output=5b3b2589bafb498e input=1e16a4d30c4d4462]*/
12585+
/*[clinic end generated code: output=4071ffee17647891 input=b1a0a14ec9448229]*/
1253312586
{
12534-
return major(device);
12587+
return major_minor_conv(major(device));
1253512588
}
1253612589

1253712590

1253812591
/*[clinic input]
12539-
os.minor -> unsigned_int
12592+
os.minor
1254012593
1254112594
device: dev_t
1254212595
/
1254312596
1254412597
Extracts a device minor number from a raw device number.
1254512598
[clinic start generated code]*/
1254612599

12547-
static unsigned int
12600+
static PyObject *
1254812601
os_minor_impl(PyObject *module, dev_t device)
12549-
/*[clinic end generated code: output=5e1a25e630b0157d input=0842c6d23f24c65e]*/
12602+
/*[clinic end generated code: output=306cb78e3bc5004f input=2f686e463682a9da]*/
1255012603
{
12551-
return minor(device);
12604+
return major_minor_conv(minor(device));
1255212605
}
1255312606

1255412607

1255512608
/*[clinic input]
1255612609
os.makedev -> dev_t
1255712610
12558-
major: int
12559-
minor: int
12611+
major: dev_t
12612+
minor: dev_t
1256012613
/
1256112614
1256212615
Composes a raw device number from the major and minor device numbers.
1256312616
[clinic start generated code]*/
1256412617

1256512618
static dev_t
12566-
os_makedev_impl(PyObject *module, int major, int minor)
12567-
/*[clinic end generated code: output=881aaa4aba6f6a52 input=4b9fd8fc73cbe48f]*/
12619+
os_makedev_impl(PyObject *module, dev_t major, dev_t minor)
12620+
/*[clinic end generated code: output=cad6125c51f5af80 input=2146126ec02e55c1]*/
1256812621
{
12622+
if (!major_minor_check(major) || !major_minor_check(minor)) {
12623+
PyErr_SetString(PyExc_OverflowError,
12624+
"Python int too large to convert to C unsigned int");
12625+
return (dev_t)-1;
12626+
}
1256912627
return makedev(major, minor);
1257012628
}
1257112629
#endif /* HAVE_DEVICE_MACROS */

0 commit comments

Comments
 (0)