10000 bpo-44184: Fix subtype_dealloc() for freed type (GH-26274) · python/cpython@298ee65 · GitHub
[go: up one dir, main page]

Skip to content

Commit 298ee65

Browse files
bpo-44184: Fix subtype_dealloc() for freed type (GH-26274)
Fix a crash at Python exit when a deallocator function removes the last strong reference to a heap type. Don't read type memory after calling basedealloc() since basedealloc() can deallocate the type and free its memory. _PyMem_IsPtrFreed() argument is now constant. (cherry picked from commit 615069e) Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 0b47049 commit 298ee65

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

Include/internal/pycore_pymem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ PyAPI_FUNC(int) _PyMem_SetDefaultAllocator(
4242
fills newly allocated memory with CLEANBYTE (0xCD) and newly freed memory
4343
with DEADBYTE (0xDD). Detect also "untouchable bytes" marked
4444
with FORBIDDENBYTE (0xFD). */
45-
static inline int _PyMem_IsPtrFreed(void *ptr)
45+
static inline int _PyMem_IsPtrFreed(const void *ptr)
4646
{
4747
uintptr_t value = (uintptr_t)ptr;
4848
#if SIZEOF_VOID_P == 8

Lib/test/test_gc.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,34 @@ def __del__(self):
13601360
# empty __dict__.
13611361
self.assertEqual(x, None)
13621362

1363+
1364+
class PythonFinalizationTests(unittest.TestCase):
1365+
def test_ast_fini(self):
1366+
# bpo-44184: Regression test for subtype_dealloc() when deallocating
1367+
# an AST instance also destroy its AST type: subtype_dealloc() must
1368+
# not access the type memory after deallocating the instance, since
1369+
# the type memory can be freed as well. The test is also related to
1370+
# _PyAST_Fini() which clears references to AST types.
1371+
code = textwrap.dedent("""
1372+
import ast
1373+
import codecs
1374+
1375+
# Small AST tree to keep their AST types alive
1376+
tree = ast.parse("def f(x, y): return 2*x-y")
1377+
x = [tree]
1378+
x.append(x)
1379+
1380+
# Put the cycle somewhere to survive until the last GC collection.
1381+
# Codec search functions are only cleared at the end of
1382+
# interpreter_clear().
1383+
def search_func(encoding):
1384+
return None
1385+
search_func.a = x
1386+
codecs.register(search_func)
1387+
""")
1388+
assert_python_ok("-c", code)
1389+
1390+
13631391
def test_main():
13641392
enabled = gc.isenabled()
13651393
gc.disable()
@@ -1369,7 +1397,11 @@ def test_main():
13691397

13701398
try:
13711399
gc.collect() # Delete 2nd generation garbage
1372-
run_unittest(GCTests, GCTogglingTests, GCCallbackTests)
1400+
run_unittest(
1401+
GCTests,
1402+
GCCallbackTests,
1403+
GCTogglingTests,
1404+
PythonFinalizationTests)
13731405
finally:
13741406
gc.set_debug(debug)
13751407
# test gc.enable() even if GC is disabled by default
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash at Python exit when a deallocator function removes the last strong
2+
reference to a heap type.
3+
Patch by Victor Stinner.

Objects/typeobject.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,15 +1341,22 @@ subtype_dealloc(PyObject *self)
13411341
if (_PyType_IS_GC(base)) {
13421342
_PyObject_GC_TRACK(self);
13431343
}
1344+
1345+
// Don't read type memory after calling basedealloc() since basedealloc()
1346+
// can deallocate the type and free its memory.
1347+
int type_needs_decref = (type->tp_flags & Py_TPFLAGS_HEAPTYPE
1348+
&& !(base->tp_flags & Py_TPFLAGS_HEAPTYPE));
1349+
13441350
assert(basedealloc);
13451351
basedealloc(self);
13461352

13471353
/* Can't reference self beyond this point. It's possible tp_del switched
13481354
our type from a HEAPTYPE to a non-HEAPTYPE, so be careful about
13491355
reference counting. Only decref if the base type is not already a heap
13501356
allocated type. Otherwise, basedealloc should have decref'd it already */
1351-
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE && !(base->tp_flags & Py_TPFLAGS_HEAPTYPE))
1352-
Py_DECREF(type);
1357+
if (type_needs_decref) {
1358+
Py_DECREF(type);
1359+
}
13531360

13541361
endlabel:
13551362
Py_TRASHCAN_END

0 commit comments

Comments
 (0)
0