Skip to content

Commit f98ec75

Browse files
[3.12] gh-89928: Fix integer conversion of device numbers (GH-31794) (GH-120054)
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)
1 parent 0e150c3 commit f98ec75

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
@@ -703,21 +703,32 @@ def test_makedev(self):
703703
self.assertEqual(posix.major(dev), major)
704704
self.assertRaises(TypeError, posix.major, float(dev))
705705
self.assertRaises(TypeError, posix.major)
706-
self.assertRaises((ValueError, OverflowError), posix.major, -1)
706+
for x in -2, 2**64, -2**63-1:
707+
self.assertRaises((ValueError, OverflowError), posix.major, x)
707708

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

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

722733
def _test_all_chown_common(self, chown_func, first_param, stat_func):
723734
"""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
@@ -19,6 +19,7 @@
1919
#include "pycore_fileutils.h" // _Py_closerange()
2020
#include "pycore_import.h" // _PyImport_ReInitLock()
2121
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
22+
#include "pycore_long.h" // _PyLong_IsNegative()
2223
#include "pycore_moduleobject.h" // _PyModule_GetState()
2324
#include "pycore_object.h" // _PyObject_LookupSpecial()
2425
#include "pycore_pystate.h" // _PyInterpreterState_GET()
@@ -920,16 +921,46 @@ _Py_Gid_Converter(PyObject *obj, gid_t *p)
920921
#endif /* MS_WINDOWS */
921922

922923

923-
#define _PyLong_FromDev PyLong_FromLongLong
924+
static PyObject *
925+
_PyLong_FromDev(dev_t dev)
926+
{
927+
#ifdef NODEV
928+
if (dev == NODEV) {
929+
return PyLong_FromLongLong((long long)dev);
930+
}
931+
#endif
932+
return PyLong_FromUnsignedLongLong((unsigned long long)dev);
933+
}
924934

925935

926936
#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS)
927937
static int
928938
_Py_Dev_Converter(PyObject *obj, void *p)
929939
{
930-
*((dev_t *)p) = PyLong_AsUnsignedLongLong(obj);
931-
if (PyErr_Occurred())
940+
#ifdef NODEV
941+
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
942+
int overflow;
943+
long long result = PyLong_AsLongLongAndOverflow(obj, &overflow);
944+
if (result == -1 && PyErr_Occurred()) {
945+
return 0;
946+
}
947+
if (!overflow && result == (long long)NODEV) {
948+
*((dev_t *)p) = NODEV;
949+
return 1;
950+
}
951+
}
952+
#endif
953+
954+
unsigned long long result = PyLong_AsUnsignedLongLong(obj);
955+
if (result == (unsigned long long)-1 && PyErr_Occurred()) {
956+
return 0;
957+
}
958+
if ((unsigned long long)(dev_t)result != result) {
959+
PyErr_SetString(PyExc_OverflowError,
960+
"Python int too large to convert to C dev_t");
932961
return 0;
962+
}
963+
*((dev_t *)p) = (dev_t)result;
933964
return 1;
934965
}
935966
#endif /* (HAVE_MKNOD && HAVE_MAKEDEV) || HAVE_DEVICE_MACROS */
@@ -11752,55 +11783,82 @@ os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device,
1175211783
#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */
1175311784

1175411785

11786+
static PyObject *
11787+
major_minor_conv(unsigned int value)
11788+
{
11789+
#ifdef NODEV
11790+
if (value == (unsigned int)NODEV) {
11791+
return PyLong_FromLong((int)NODEV);
11792+
}
11793+
#endif
11794+
return PyLong_FromUnsignedLong(value);
11795+
}
11796+
11797+
static int
11798+
major_minor_check(dev_t value)
11799+
{
11800+
#ifdef NODEV
11801+
if (value == NODEV) {
11802+
return 1;
11803+
}
11804+
#endif
11805+
return (dev_t)(unsigned int)value == value;
11806+
}
11807+
1175511808
#ifdef HAVE_DEVICE_MACROS
1175611809
/*[clinic input]
11757-
os.major -> unsigned_int
11810+
os.major
1175811811
1175911812
device: dev_t
1176011813
/
1176111814
1176211815
Extracts a device major number from a raw device number.
1176311816
[clinic start generated code]*/
1176411817

11765-
static unsigned int
11818+
static PyObject *
1176611819
os_major_impl(PyObject *module, dev_t device)
11767-
/*[clinic end generated code: output=5b3b2589bafb498e input=1e16a4d30c4d4462]*/
11820+
/*[clinic end generated code: output=4071ffee17647891 input=b1a0a14ec9448229]*/
1176811821
{
11769-
return major(device);
11822+
return major_minor_conv(major(device));
1177011823
}
1177111824

1177211825

1177311826
/*[clinic input]
11774-
os.minor -> unsigned_int
11827+
os.minor
1177511828
1177611829
device: dev_t
1177711830
/
1177811831
1177911832
Extracts a device minor number from a raw device number.
1178011833
[clinic start generated code]*/
1178111834

11782-
static unsigned int
11835+
static PyObject *
1178311836
os_minor_impl(PyObject *module, dev_t device)
11784-
/*[clinic end generated code: output=5e1a25e630b0157d input=0842c6d23f24c65e]*/
11837+
/*[clinic end generated code: output=306cb78e3bc5004f input=2f686e463682a9da]*/
1178511838
{
11786-
return minor(device);
11839+
return major_minor_conv(minor(device));
1178711840
}
1178811841

1178911842

1179011843
/*[clinic input]
1179111844
os.makedev -> dev_t
1179211845
11793-
major: int
11794-
minor: int
11846+
major: dev_t
11847+
minor: dev_t
1179511848
/
1179611849
1179711850
Composes a raw device number from the major and minor device numbers.
1179811851
[clinic start generated code]*/
1179911852

1180011853
static dev_t
11801-
os_makedev_impl(PyObject *module, int major, int minor)
11802-
/*[clinic end generated code: output=881aaa4aba6f6a52 input=4b9fd8fc73cbe48f]*/
11854+
os_makedev_impl(PyObject *module, dev_t major, dev_t minor)
11855+
/*[clinic end generated code: output=cad6125c51f5af80 input=2146126ec02e55c1]*/
1180311856
{
11857+
if (!major_minor_check(major) || !major_minor_check(minor)) {
11858+
PyErr_SetString(PyExc_OverflowError,
11859+
"Python int too large to convert to C unsigned int");
11860+
return (dev_t)-1;
11861+
}
1180411862
return makedev(major, minor);
1180511863
}
1180611864
#endif /* HAVE_DEVICE_MACROS */

0 commit comments

Comments
 (0)