mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	[3.9] bpo-41654: Fix deallocator of MemoryError to account for subclasses (GH-22020) (GH-22045)
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 9b648a95cc)
Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									4217b3c128
								
							
						
					
					
						commit
						d14775ddbb
					
				
					 3 changed files with 45 additions and 2 deletions
				
			
		|  | @ -1,6 +1,7 @@ | |||
| # Python test set -- part 5, built-in exceptions | ||||
| 
 | ||||
| import copy | ||||
| import gc | ||||
| import os | ||||
| import sys | ||||
| import unittest | ||||
|  | @ -1327,6 +1328,36 @@ def test_assert_shadowing(self): | |||
|             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. | ||||
|  | @ -2282,8 +2282,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 */ | ||||
|  | @ -2303,8 +2307,14 @@ MemoryError_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |||
| static void | ||||
| MemoryError_dealloc(PyBaseExceptionObject *self) | ||||
| { | ||||
|     _PyObject_GC_UNTRACK(self); | ||||
|     BaseException_clear(self); | ||||
| 
 | ||||
|     if (!Py_IS_TYPE(self, (PyTypeObject *) 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 { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pablo Galindo
						Pablo Galindo