From 42c0291e1dfc45387dc55611c836b524a381d21a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 30 Nov 2022 17:28:32 +0100 Subject: [PATCH 1/2] gh-99845: _PySys_GetSizeOf() uses size_t The _PySys_GetSizeOf() function now uses an unsigned type (size_t) to compute the size, rather than a signed type (Py_ssize_t). * _PySys_GetSizeOf() now checks for integer overflow when adding the header size. * Reformat also _PySys_GetSizeOf(). --- Lib/test/test_sys.py | 14 ++++++++------ Python/sysmodule.c | 33 +++++++++++++++------------------ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 2403c7c815f2c0..839d54c35e4709 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1297,17 +1297,19 @@ def __sizeof__(self): self.assertRaises(TypeError, sys.getsizeof, FloatSizeof()) self.assertIs(sys.getsizeof(FloatSizeof(), sentinel), sentinel) + # size_t maximum + header_size = self.gc_headsize * 2 + umaxsize = (sys.maxsize * 2 + 1) - header_size + class OverflowSizeof(int): def __sizeof__(self): return int(self) - self.assertEqual(sys.getsizeof(OverflowSizeof(sys.maxsize)), - sys.maxsize + self.gc_headsize*2) + self.assertEqual(sys.getsizeof(OverflowSizeof(umaxsize)), + umaxsize + header_size) with self.assertRaises(OverflowError): - sys.getsizeof(OverflowSizeof(sys.maxsize + 1)) - with self.assertRaises(ValueError): - sys.getsizeof(OverflowSizeof(-1)) + sys.getsizeof(OverflowSizeof(umaxsize + 1)) with self.assertRaises((ValueError, OverflowError)): - sys.getsizeof(OverflowSizeof(-sys.maxsize - 1)) + sys.getsizeof(OverflowSizeof(-1)) def test_default(self): size = test.support.calcvobjsize diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 88f806e616f27e..e3e1cdaec25705 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1741,44 +1741,41 @@ sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits) size_t _PySys_GetSizeOf(PyObject *o) { - PyObject *res = NULL; - PyObject *method; - Py_ssize_t size; - PyThreadState *tstate = _PyThreadState_GET(); - /* Make sure the type is initialized. float gets initialized late */ if (PyType_Ready(Py_TYPE(o)) < 0) { return (size_t)-1; } - method = _PyObject_LookupSpecial(o, &_Py_ID(__sizeof__)); + PyObject *method = _PyObject_LookupSpecial(o, &_Py_ID(__sizeof__)); + PyThreadState *tstate = _PyThreadState_GET(); if (method == NULL) { if (!_PyErr_Occurred(tstate)) { _PyErr_Format(tstate, PyExc_TypeError, "Type %.100s doesn't define __sizeof__", Py_TYPE(o)->tp_name); } - } - else { - res = _PyObject_CallNoArgs(method); - Py_DECREF(method); + return (size_t)-1; } - if (res == NULL) + PyObject *res = _PyObject_CallNoArgs(method); + Py_DECREF(method); + if (res == NULL) { return (size_t)-1; + } - size = PyLong_AsSsize_t(res); + size_t size = PyLong_AsSize_t(res); Py_DECREF(res); - if (size == -1 && _PyErr_Occurred(tstate)) + if (size == (size_t)-1 && _PyErr_Occurred(tstate)) { return (size_t)-1; + } - if (size < 0) { - _PyErr_SetString(tstate, PyExc_ValueError, - "__sizeof__() should return >= 0"); + size_t header_size = _PyType_PreHeaderSize(Py_TYPE(o)); + if (size > SIZE_MAX - header_size) { + PyErr_SetString(PyExc_OverflowError, + "size greater than size_t maximum"); return (size_t)-1; } - - return (size_t)size + _PyType_PreHeaderSize(Py_TYPE(o)); + return header_size + size; } static PyObject * From 01c6f4981671d382148014c817ca49f332353663 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 1 Dec 2022 11:46:32 +0100 Subject: [PATCH 2/2] Enhance tests --- Lib/test/test_sys.py | 20 +++++++++++++++----- Python/sysmodule.c | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 839d54c35e4709..c2abde26d539e2 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1269,6 +1269,15 @@ def setUp(self): check_sizeof = test.support.check_sizeof + def test_sizeof_method(self): + class Sizeof(int): + def __sizeof__(self): + return int(self) + + header_size = self.gc_headsize * 2 + self.assertEqual(sys.getsizeof(Sizeof(0)), header_size) + self.assertEqual(sys.getsizeof(Sizeof(100)), 100 + header_size) + def test_gc_head_size(self): # Check that the gc header size is added to objects tracked by the gc. vsize = test.support.calcvobjsize @@ -1299,16 +1308,17 @@ def __sizeof__(self): # size_t maximum header_size = self.gc_headsize * 2 - umaxsize = (sys.maxsize * 2 + 1) - header_size + maxsize = (sys.maxsize * 2 + 1) - header_size class OverflowSizeof(int): def __sizeof__(self): return int(self) - self.assertEqual(sys.getsizeof(OverflowSizeof(umaxsize)), - umaxsize + header_size) + + self.assertEqual(sys.getsizeof(OverflowSizeof(maxsize)), + maxsize + header_size) + with self.assertRaises(OverflowError): + sys.getsizeof(OverflowSizeof(maxsize + 1)) with self.assertRaises(OverflowError): - sys.getsizeof(OverflowSizeof(umaxsize + 1)) - with self.assertRaises((ValueError, OverflowError)): sys.getsizeof(OverflowSizeof(-1)) def test_default(self): diff --git a/Python/sysmodule.c b/Python/sysmodule.c index e3e1cdaec25705..10f9a43ed2eeb2 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1772,7 +1772,7 @@ _PySys_GetSizeOf(PyObject *o) size_t header_size = _PyType_PreHeaderSize(Py_TYPE(o)); if (size > SIZE_MAX - header_size) { PyErr_SetString(PyExc_OverflowError, - "size greater than size_t maximum"); + "__sizeof__() greater than size_t maximum"); return (size_t)-1; } return header_size + size;