From ec88f679c0f056006042f58d0cffb99cf83ca8be Mon Sep 17 00:00:00 2001 From: Christian Aurich Zanettini Martins Date: Mon, 12 Oct 2026 23:32:50 -0300 Subject: [PATCH] gh-159285: Fix crash when copying an empty dict fails with MemoryError copy_lock_held_untracked() asserted that the new empty dict was not tracked by the GC without checking whether its allocation had failed, so a debug build dereferenced NULL instead of raising MemoryError. This affected dict.copy(), PyDict_Copy() and _PyDict_CopyAsDict(), which type() uses for the class namespace. --- Lib/test/test_dict.py | 7 +++++++ .../2026-10-12-23-32-50.gh-issue-159285.nlLuwQ.rst | 2 ++ Objects/dictobject.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-12-23-32-50.gh-issue-159285.nlLuwQ.rst diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 26bdaa9d6a35b7..9d013e3d2caa1c 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -479,6 +479,13 @@ def test_copy_noncompact(self): d2 = d.copy() self.assertEqual(d2, d) + @support.nomemtest + def test_copy_empty_no_memory(self): + import _testcapi + d = {} + with self.assertRaises(MemoryError): + _testcapi.call_with_nomemory(0, 0, d.copy) + def test_get(self): d = {} self.assertIs(d.get('c'), None) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-12-23-32-50.gh-issue-159285.nlLuwQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-12-23-32-50.gh-issue-159285.nlLuwQ.rst new file mode 100644 index 00000000000000..0cb795f8309f73 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-12-23-32-50.gh-issue-159285.nlLuwQ.rst @@ -0,0 +1,2 @@ +Fix a crash in a debug build when copying an empty :class:`dict` fails with +:exc:`MemoryError`. Patch by Christian Aurich Zanettini Martins. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index eed9bd92c55bf2..13c9e2e4b395ab 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -4497,7 +4497,7 @@ copy_lock_held_untracked(PyObject *o, int as_frozendict) else { d = dict_new_untracked(&PyDict_Type); } - assert(!_PyObject_GC_IS_TRACKED(d)); + assert(d == NULL || !_PyObject_GC_IS_TRACKED(d)); return d; }