From: Pablo Galindo Date: Tue, 1 Sep 2020 20:40:48 +0000 (+0100) Subject: [3.8] [3.9] bpo-41654: Fix deallocator of MemoryError to account for subclasses ... X-Git-Tag: v3.8.6rc1~16 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=77f4000ae0d43a2685face80e7f14d4aba053973;p=thirdparty%2FPython%2Fcpython.git [3.8] [3.9] bpo-41654: Fix deallocator of MemoryError to account for subclasses (GH-22020) (GH-22046) When allocating MemoryError classes, there is some logic to use pre-allocated instances in a freelist only if the type that is being allocated is not a subclass of MemoryError. Unfortunately in the destructor this logic is not present so the freelist is altered even with subclasses of MemoryError.. (cherry picked from commit 9b648a95ccb4c3b14f1e87158f5c9f5dbb2f62c0) Co-authored-by: Pablo Galindo . (cherry picked from commit 87e91ae2e5f81e096c32839f211c68a749a4435a) Co-authored-by: Pablo Galindo --- diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 3a3225315736..457b46e01ce3 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1,6 +1,7 @@ # Python test set -- part 5, built-in exceptions import copy +import gc import os import sys import unittest @@ -1297,6 +1298,35 @@ class ExceptionTests(unittest.TestCase): next(i) next(i) + def test_memory_error_subclasses(self): + # bpo-41654: MemoryError instances use a freelist of objects that are + # linked using the 'dict' attribute when they are inactive/dead. + # Subclasses of MemoryError should not participate in the freelist + # schema. This test creates a MemoryError object and keeps it alive + # (therefore advancing the freelist) and then it creates and destroys a + # subclass object. Finally, it checks that creating a new MemoryError + # succeeds, proving that the freelist is not corrupted. + + class TestException(MemoryError): + pass + + try: + raise MemoryError + except MemoryError as exc: + inst = exc + + try: + raise TestException + except Exception: + pass + + for _ in range(10): + try: + raise MemoryError + except MemoryError as exc: + pass + + gc_collect() class ImportErrorTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst b/Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst new file mode 100644 index 000000000000..e05c3133e126 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst @@ -0,0 +1,2 @@ +Fix a crash that occurred when destroying subclasses of +:class:`MemoryError`. Patch by Pablo Galindo. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 38723d93653c..966983810cd1 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -2268,8 +2268,12 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyBaseExceptionObject *self; - if (type != (PyTypeObject *) PyExc_MemoryError) + /* If this is a subclass of MemoryError, don't use the freelist + * and just return a fresh object */ + if (type != (PyTypeObject *) PyExc_MemoryError) { return BaseException_new(type, args, kwds); + } + if (memerrors_freelist == NULL) return BaseException_new(type, args, kwds); /* Fetch object from freelist and revive it */ @@ -2289,8 +2293,14 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static void MemoryError_dealloc(PyBaseExceptionObject *self) { - _PyObject_GC_UNTRACK(self); BaseException_clear(self); + + if (Py_TYPE(self) != PyExc_MemoryError) { + return Py_TYPE(self)->tp_free((PyObject *)self); + } + + _PyObject_GC_UNTRACK(self); + if (memerrors_numfree >= MEMERRORS_SAVE) Py_TYPE(self)->tp_free((PyObject *)self); else {