From da2f9d1417a7d28df6e1ced87d64ecf28acb0a5f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 20 Mar 2024 16:44:05 +0200 Subject: [3.12] gh-117021: Fix integer overflow in PyLong_AsPid() on non-Windows 64-bit platforms (GH-117064) (GH-117070) (cherry picked from commit 519b2ae22b54760475bbf62b9558d453c703f9c6) --- Include/Python.h | 6 +++--- Include/longobject.h | 19 +++++++++++++++++- Lib/test/test_capi/test_long.py | 23 ++++++++++++++++++++++ .../2024-03-20-13-13-22.gh-issue-117021.0Q5jBx.rst | 2 ++ Modules/_testcapi/long.c | 13 ++++++++++++ Modules/_testcapimodule.c | 1 + 6 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-03-20-13-13-22.gh-issue-117021.0Q5jBx.rst diff --git a/Include/Python.h b/Include/Python.h index 52a7aac..5eddda6 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -49,6 +49,9 @@ #include "bytearrayobject.h" #include "bytesobject.h" #include "unicodeobject.h" +#include "cpython/initconfig.h" +#include "pystate.h" +#include "pyerrors.h" #include "longobject.h" #include "cpython/longintrepr.h" #include "boolobject.h" @@ -74,8 +77,6 @@ #include "sliceobject.h" #include "cpython/cellobject.h" #include "iterobject.h" -#include "cpython/initconfig.h" -#include "pystate.h" #include "cpython/genobject.h" #include "descrobject.h" #include "genericaliasobject.h" @@ -85,7 +86,6 @@ #include "cpython/picklebufobject.h" #include "cpython/pytime.h" #include "codecs.h" -#include "pyerrors.h" #include "pythread.h" #include "cpython/context.h" #include "modsupport.h" diff --git a/Include/longobject.h b/Include/longobject.h index e090dd0..c8b7497 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -34,7 +34,24 @@ PyAPI_FUNC(PyObject *) PyLong_GetInfo(void); #if !defined(SIZEOF_PID_T) || SIZEOF_PID_T == SIZEOF_INT #define _Py_PARSE_PID "i" #define PyLong_FromPid PyLong_FromLong -#define PyLong_AsPid PyLong_AsLong +# ifndef Py_LIMITED_API +# define PyLong_AsPid _PyLong_AsInt +# elif SIZEOF_INT == SIZEOF_LONG +# define PyLong_AsPid PyLong_AsLong +# else +static inline int +PyLong_AsPid(PyObject *obj) +{ + int overflow; + long result = PyLong_AsLongAndOverflow(obj, &overflow); + if (overflow || result > INT_MAX || result < INT_MIN) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)result; +} +# endif #elif SIZEOF_PID_T == SIZEOF_LONG #define _Py_PARSE_PID "l" #define PyLong_FromPid PyLong_FromLong diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py index 8261cc3..39fef24 100644 --- a/Lib/test/test_capi/test_long.py +++ b/Lib/test/test_capi/test_long.py @@ -400,6 +400,29 @@ class LongTests(unittest.TestCase): self.assertRaises(OverflowError, asvoidptr, -2**1000) # CRASHES asvoidptr(NULL) + def test_long_aspid(self): + # Test PyLong_AsPid() + aspid = _testcapi.pylong_aspid + from _testcapi import SIZEOF_PID_T + bits = 8 * SIZEOF_PID_T + PID_T_MIN = -2**(bits-1) + PID_T_MAX = 2**(bits-1) - 1 + # round trip (object -> long -> object) + for value in (PID_T_MIN, PID_T_MAX, -1, 0, 1, 1234): + with self.subTest(value=value): + self.assertEqual(aspid(value), value) + + self.assertEqual(aspid(IntSubclass(42)), 42) + self.assertEqual(aspid(Index(42)), 42) + self.assertEqual(aspid(MyIndexAndInt()), 10) + + self.assertRaises(OverflowError, aspid, PID_T_MIN - 1) + self.assertRaises(OverflowError, aspid, PID_T_MAX + 1) + self.assertRaises(TypeError, aspid, 1.0) + self.assertRaises(TypeError, aspid, b'2') + self.assertRaises(TypeError, aspid, '3') + self.assertRaises(SystemError, aspid, NULL) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/C API/2024-03-20-13-13-22.gh-issue-117021.0Q5jBx.rst b/Misc/NEWS.d/next/C API/2024-03-20-13-13-22.gh-issue-117021.0Q5jBx.rst new file mode 100644 index 0000000..2f93e1e --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-03-20-13-13-22.gh-issue-117021.0Q5jBx.rst @@ -0,0 +1,2 @@ +Fix integer overflow in :c:func:`PyLong_AsPid` on non-Windows 64-bit +platforms. diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c index 213c676..9c5a0e3 100644 --- a/Modules/_testcapi/long.c +++ b/Modules/_testcapi/long.c @@ -750,6 +750,18 @@ pylong_asvoidptr(PyObject *module, PyObject *arg) return Py_NewRef((PyObject *)value); } +static PyObject * +pylong_aspid(PyObject *module, PyObject *arg) +{ + NULLABLE(arg); + pid_t value = PyLong_AsPid(arg); + if (value == -1 && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromPid(value); +} + + static PyMethodDef test_methods[] = { {"test_long_and_overflow", test_long_and_overflow, METH_NOARGS}, {"test_long_api", test_long_api, METH_NOARGS}, @@ -778,6 +790,7 @@ static PyMethodDef test_methods[] = { {"pylong_as_size_t", pylong_as_size_t, METH_O}, {"pylong_asdouble", pylong_asdouble, METH_O}, {"pylong_asvoidptr", pylong_asvoidptr, METH_O}, + {"pylong_aspid", pylong_aspid, METH_O}, {NULL}, }; diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 48c8f92..e906658 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3986,6 +3986,7 @@ PyInit__testcapi(void) PyModule_AddObject(m, "SIZEOF_WCHAR_T", PyLong_FromSsize_t(sizeof(wchar_t))); PyModule_AddObject(m, "SIZEOF_VOID_P", PyLong_FromSsize_t(sizeof(void*))); PyModule_AddObject(m, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t))); + PyModule_AddObject(m, "SIZEOF_PID_T", PyLong_FromSsize_t(sizeof(pid_t))); PyModule_AddObject(m, "Py_Version", PyLong_FromUnsignedLong(Py_Version)); Py_INCREF(&PyInstanceMethod_Type); PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type); -- cgit v0.12