Crash report
What happened?
import _testcapi
def make_class():
class C:
def f(self):
self.x = 1
return C
for start in range(100):
_testcapi.set_nomemory(start, start + 1)
try:
x = make_class()
except MemoryError:
pass
fish: Job 1, './python.exe staticattrs.py' terminated by signal SIGSEGV (Address boundary error)
If an allocation failure happens when defining a class that has a non-empty __static_attributes__, then you can get a null pointer deref.
Root cause
Early during the class build process, __static_attributes__ is correctly populated with the list of detected static attributes.
Then later during PyType_Ready(): type_ready_managed_dict is called to make the shared key cache.
This calls _PyDict_NewKeysForClass and this line:
|
PyDictKeysObject *keys = new_keys_object(NEXT_LOG2_SHARED_KEYS_MAX_SIZE, 1); |
Tries to allocate a keys object, and if that allocation fails, just clears the MemoryError and continues, skipping out some keys setup steps.
However, later on in that _PyDict_NewKeysForClass it fetches the __static_attributes__ tuple and tries to populate them into the keys object:
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(attrs); i++) { |
|
PyObject *key = PyTuple_GET_ITEM(attrs, i); |
|
Py_hash_t hash; |
|
if (PyUnicode_CheckExact(key) && (hash = unicode_get_hash(key)) != -1) { |
|
if (insert_split_key(keys, key, hash) == DKIX_EMPTY) { |
|
break; |
|
} |
And insert_split_key reasonably assumes that keys is a valid Keys object, resulting in a relative pointer from NULL dereferenec.
Stacktrace
frame #0 _DK_ENTRIES(dk=<unavailable>) at pycore_dict.h:269
frame #1 do_lookup(mp=0x0, dk=0x0, ...) at dictobject.c:1090
frame #2 unicodekeys_lookup_unicode(dk=0x0, ...) at dictobject.c:1183
frame #3 insert_split_key(keys=0x0, ...) at dictobject.c:1931
frame #4 _PyDict_NewKeysForClass(cls=0x0000aaaaabfffa70) at dictobject.c:7236
frame #5 type_ready_managed_dict(type=0x0000aaaaabfffa70) at typeobject.c:9473
frame #6 type_ready(...) at typeobject.c:9578
frame #7 PyType_Ready(...) at typeobject.c:9622
Interestingly bisect failed here, because before this change, there was a different allocation failure segfault from class definition, so either this one is shadowing the other on (I'll check separately) or the other issue has already/accidentally been fixed.
Suggested fix.
Given that _PyDict_NewKeysForClass returns keys at the end anyway, it seems reasonable to just return NULL from the keys == NULL branch here:
|
if (keys == NULL) { |
|
PyErr_Clear(); |
|
} |
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.16.0a0 (heads/main-dirty:639a552, Jul 4 2026, 21:13:52) [Clang 22.1.6 ]
Linked PRs
Crash report
What happened?
If an allocation failure happens when defining a class that has a non-empty
__static_attributes__, then you can get a null pointer deref.Root cause
Early during the class build process,
__static_attributes__is correctly populated with the list of detected static attributes.Then later during PyType_Ready():
type_ready_managed_dictis called to make the shared key cache.This calls
_PyDict_NewKeysForClassand this line:cpython/Objects/dictobject.c
Line 7219 in 9f9787d
Tries to allocate a keys object, and if that allocation fails, just clears the
MemoryErrorand continues, skipping out some keys setup steps.However, later on in that
_PyDict_NewKeysForClassit fetches the__static_attributes__tuple and tries to populate them into the keys object:cpython/Objects/dictobject.c
Lines 7232 to 7238 in 9f9787d
And
insert_split_keyreasonably assumes thatkeysis a valid Keys object, resulting in a relative pointer from NULL dereferenec.Stacktrace
Interestingly bisect failed here, because before this change, there was a different allocation failure segfault from class definition, so either this one is shadowing the other on (I'll check separately) or the other issue has already/accidentally been fixed.
Suggested fix.
Given that _PyDict_NewKeysForClass returns
keysat the end anyway, it seems reasonable to just return NULL from thekeys == NULLbranch here:cpython/Objects/dictobject.c
Lines 7220 to 7222 in 9f9787d
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.16.0a0 (heads/main-dirty:639a552, Jul 4 2026, 21:13:52) [Clang 22.1.6 ]
Linked PRs
new_keys_objectcall fails in_PyDict_NewKeysForClass#153183