[Python-checkins] [3.8] [3.9] bpo-41654: Fix deallocator of MemoryError to account for subclasses (GH-22020) (GH-22046)
Pablo Galindo
webhook-mailer at python.org
Tue Sep 1 16:41:04 EDT 2020
https://github.com/python/cpython/commit/77f4000ae0d43a2685face80e7f14d4aba053973
commit: 77f4000ae0d43a2685face80e7f14d4aba053973
branch: 3.8
author: Pablo Galindo <Pablogsal at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-09-01T21:40:48+01:00
summary:
[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 <Pablogsal at gmail.com>.
(cherry picked from commit 87e91ae2e5f81e096c32839f211c68a749a4435a)
Co-authored-by: Pablo Galindo <Pablogsal at gmail.com>
files:
A Misc/NEWS.d/next/Core and Builtins/2020-08-30-20-38-33.bpo-41654.HtnhAM.rst
M Lib/test/test_exceptions.py
M Objects/exceptions.c
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 3a32253157369..457b46e01ce31 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 @@ def g():
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 0000000000000..e05c3133e1262
--- /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 38723d93653c3..966983810cd1e 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 {
More information about the Python-checkins
mailing list