[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 commit9b648a95cc
) Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>. (cherry picked from commit87e91ae2e5
) Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
This commit is contained in:
parent
38e32872eb
commit
77f4000ae0
|
@ -1,6 +1,7 @@
|
||||||
# Python test set -- part 5, built-in exceptions
|
# Python test set -- part 5, built-in exceptions
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import gc
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
@ -1297,6 +1298,35 @@ class ExceptionTests(unittest.TestCase):
|
||||||
next(i)
|
next(i)
|
||||||
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):
|
class ImportErrorTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix a crash that occurred when destroying subclasses of
|
||||||
|
:class:`MemoryError`. Patch by Pablo Galindo.
|
|
@ -2268,8 +2268,12 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
{
|
{
|
||||||
PyBaseExceptionObject *self;
|
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);
|
return BaseException_new(type, args, kwds);
|
||||||
|
}
|
||||||
|
|
||||||
if (memerrors_freelist == NULL)
|
if (memerrors_freelist == NULL)
|
||||||
return BaseException_new(type, args, kwds);
|
return BaseException_new(type, args, kwds);
|
||||||
/* Fetch object from freelist and revive it */
|
/* Fetch object from freelist and revive it */
|
||||||
|
@ -2289,8 +2293,14 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
static void
|
static void
|
||||||
MemoryError_dealloc(PyBaseExceptionObject *self)
|
MemoryError_dealloc(PyBaseExceptionObject *self)
|
||||||
{
|
{
|
||||||
_PyObject_GC_UNTRACK(self);
|
|
||||||
BaseException_clear(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)
|
if (memerrors_numfree >= MEMERRORS_SAVE)
|
||||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Reference in New Issue