bpo-41654: Fix deallocator of MemoryError to account for subclasses (GH-22020)
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.
This commit is contained in:
parent
6844b56176
commit
9b648a95cc
|
@ -1,6 +1,7 @@
|
|||
# Python test set -- part 5, built-in exceptions
|
||||
|
||||
import copy
|
||||
import gc
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
@ -1330,6 +1331,36 @@ class ExceptionTests(unittest.TestCase):
|
|||
del AssertionError
|
||||
self.fail('Expected exception')
|
||||
|
||||
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):
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Fix a crash that occurred when destroying subclasses of
|
||||
:class:`MemoryError`. Patch by Pablo Galindo.
|
|
@ -2286,8 +2286,11 @@ 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);
|
||||
}
|
||||
|
||||
struct _Py_exc_state *state = get_exc_state();
|
||||
if (state->memerrors_freelist == NULL) {
|
||||
|
@ -2313,9 +2316,16 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
static void
|
||||
MemoryError_dealloc(PyBaseExceptionObject *self)
|
||||
{
|
||||
_PyObject_GC_UNTRACK(self);
|
||||
BaseException_clear(self);
|
||||
|
||||
/* If this is a subclass of MemoryError, we don't need to
|
||||
* do anything in the free-list*/
|
||||
if (!Py_IS_TYPE(self, (PyTypeObject *) PyExc_MemoryError)) {
|
||||
return Py_TYPE(self)->tp_free((PyObject *)self);
|
||||
}
|
||||
|
||||
_PyObject_GC_UNTRACK(self);
|
||||
|
||||
struct _Py_exc_state *state = get_exc_state();
|
||||
if (state->memerrors_numfree >= MEMERRORS_SAVE) {
|
||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||
|
|
Loading…
Reference in New Issue