diff --git a/Include/cpython/object.h b/Include/cpython/object.h index c93931634fe..7512bb70c76 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -229,6 +229,7 @@ struct _typeobject { /* bitset of which type-watchers care about this type */ unsigned char tp_watched; + uint16_t tp_versions_used; }; /* This struct is used by the specializer diff --git a/Lib/test/test_type_cache.py b/Lib/test/test_type_cache.py index 295df78a173..58572c6f4d3 100644 --- a/Lib/test/test_type_cache.py +++ b/Lib/test/test_type_cache.py @@ -79,6 +79,19 @@ class TypeCacheTests(unittest.TestCase): _clear_type_cache() + def test_per_class_limit(self): + class C: + x = 0 + + type_assign_version(C) + orig_version = type_get_version(C) + for i in range(1001): + C.x = i + type_assign_version(C) + + new_version = type_get_version(C) + self.assertEqual(new_version, 0) + @support.cpython_only class TypeCacheWithSpecializationTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-02-02-05-27-48.gh-issue-113462.VMml8q.rst b/Misc/NEWS.d/next/Core and Builtins/2024-02-02-05-27-48.gh-issue-113462.VMml8q.rst new file mode 100644 index 00000000000..1a401ecebf0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-02-02-05-27-48.gh-issue-113462.VMml8q.rst @@ -0,0 +1,2 @@ +Limit the number of versions that a single class can use. Prevents a few +wayward classes using up all the version numbers. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index a850473cad8..e220d10ce56 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -908,6 +908,8 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { } } +#define MAX_VERSIONS_PER_CLASS 1000 + static int assign_version_tag(PyInterpreterState *interp, PyTypeObject *type) { @@ -922,7 +924,10 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type) if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) { return 0; } - + if (type->tp_versions_used >= MAX_VERSIONS_PER_CLASS) { + return 0; + } + type->tp_versions_used++; if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) { /* static types */ if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {