Missing `PyErr_NoMemory` in `_zoneinfo` `load_data` and `ts_to_local`
Crash report
What happened?
It's possible to cause assertion errors due to calls returning NULL on OOM in _zoneinfo.
Automated diagnosis:
Multiple PyMem_Malloc/PyMem_Calloc calls in load_data (including ts_to_local at line 2130) return NULL on OOM and goto error without setting an exception. The caller returns NULL without exception → assertion failure.
File: Modules/_zoneinfo.c, lines 1029-1137 (load_data), 2130 (ts_to_local)
MRE for load_data:
import _testcapi, sys for mod in ["zoneinfo", "_zoneinfo"]: if mod in sys.modules: del sys.modules[mod] for n in range(1, 50): for mod in ["zoneinfo", "_zoneinfo"]: if mod in sys.modules: del sys.modules[mod] print(n) _testcapi.set_nomemory(n, 0) try: from zoneinfo import ZoneInfo z = ZoneInfo("UTC") _testcapi.remove_mem_hooks() break except MemoryError: _testcapi.remove_mem_hooks()
MRE for ts_to_local:
import _testcapi from zoneinfo import ZoneInfo # Warm up imports — we only want OOM during zone construction _ = ZoneInfo("UTC") del _ # Clear cache so America/New_York gets re-loaded from TZif data ZoneInfo.clear_cache() # Persistent failure from allocation #9 onward. # Early allocs (import machinery, arg parsing) succeed; # the PyMem_Malloc in load_data/ts_to_local fails. _testcapi.set_nomemory(9, 0) z = ZoneInfo("America/New_York") # crashes: assertion `obj != NULL'
Backtrace:
python: ./Include/internal/pycore_stackref.h:554: _PyStackRef PyStackRef_FromPyObjectSteal(PyObject *): Assertion `obj != NULL' failed.
Program received signal SIGABRT, Aborted.
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
#2 __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:100
#3 0x00007ffff7c45e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4 0x00007ffff7c28888 in __GI_abort () at ./stdlib/abort.c:77
#5 0x00007ffff7c287f0 in __assert_fail_base (fmt=<optimized out>, assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:118
#6 0x00007ffff7c3c19f in __assert_fail (assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:127
#7 0x000055555587fd55 in PyStackRef_FromPyObjectSteal (obj=<optimized out>) at ./Include/internal/pycore_stackref.h:554
#8 0x0000555555842cd0 in _PyEval_EvalFrameDefault (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, frame=<optimized out>, frame@entry=0x7ffff7fa70a0, throwflag=throwflag@entry=0)
at Python/generated_cases.c.h:292
#9 0x000055555583f08b in _PyEval_EvalFrame (tstate=0x555555d99c08 <_PyRuntime+360664>, frame=0x7ffff7fa70a0, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#10 _PyEval_Vector (tstate=0x555555d99c08 <_PyRuntime+360664>, func=0x7ffff75e9190, locals=0x0, args=0x7fffffffb310, argcount=2, kwnames=0x0) at Python/ceval.c:2130
#11 0x00005555556a2ebe in _PyObject_VectorcallTstate (tstate=0x555555d99c08 <_PyRuntime+360664>, callable=callable@entry=0x7ffff75e9190, args=args@entry=0x7fffffffb310,
nargsf=nargsf@entry=2, kwnames=kwnames@entry=0x0) at ./Include/internal/pycore_call.h:144
#12 0x00005555556a625a in object_vacall (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, base=base@entry=0x0, callable=callable@entry=0x7ffff75e9190,
vargs=vargs@entry=0x7fffffffb460) at Objects/call.c:823
#13 0x00005555556a5f0b in PyObject_CallMethodObjArgs (obj=0x7ffff75b08c0, name=0x555555d57470 <_PyRuntime+88384>) at Objects/call.c:960
#14 0x000055555595136b in import_find_and_load (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, abs_name=abs_name@entry=0x7ffff74f86d0) at Python/import.c:4125
#15 0x0000555555950f66 in PyImport_ImportModuleLevelObject (name=name@entry=0x7ffff74f86d0, globals=globals@entry=0x7ffff746a4b0, locals=locals@entry=0x7ffff746a4b0,
fromlist=0x7ffff7477cf0, level=level@entry=0) at Python/import.c:4241
#16 0x00005555558f7265 in _PyEval_ImportNameWithImport (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, import_func=0x7ffff75be9f0, globals=globals@entry=0x7ffff746a4b0,
locals=locals@entry=0x7ffff746a4b0, name=name@entry=0x7ffff74f86d0, fromlist=fromlist@entry=0x7ffff7477cf0, level=0x555555d49188 <_PyRuntime+30296>) at Python/ceval.c:2975
#17 0x00005555558848bf in _PyEval_ImportName (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, builtins=0x7ffff75be930, globals=0x7ffff746a4b0, locals=0x7ffff746a4b0,
name=0x7ffff74f86d0, fromlist=0x7ffff7477cf0, level=0x555555d49188 <_PyRuntime+30296>) at Python/ceval.c:2954
#18 0x000055555586141a in _PyEval_EvalFrameDefault (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, frame=<optimized out>, frame@entry=0x7ffff7fa7020, throwflag=throwflag@entry=0)
at Python/generated_cases.c.h:6460
#19 0x000055555583f08b in _PyEval_EvalFrame (tstate=0x555555d99c08 <_PyRuntime+360664>, frame=0x7ffff7fa7020, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#20 _PyEval_Vector (tstate=tstate@entry=0x555555d99c08 <_PyRuntime+360664>, func=func@entry=0x7ffff7466690, locals=locals@entry=0x7ffff746a4b0, args=args@entry=0x0,
argcount=argcount@entry=0, kwnames=kwnames@entry=0x0) at Python/ceval.c:2130
#21 0x000055555583ee1e in PyEval_EvalCode (co=co@entry=0x555555f3baf0, globals=globals@entry=0x7ffff746a4b0, locals=locals@entry=0x7ffff746a4b0) at Python/ceval.c:686
#22 0x00005555559c8f8e in run_eval_code_obj (tstate=0x555555d99c08 <_PyRuntime+360664>, co=co@entry=0x555555f3baf0, globals=globals@entry=0x7ffff746a4b0, locals=locals@entry=0x7ffff746a4b0)
at Python/pythonrun.c:1368
#23 0x00005555559c8adb in run_mod (mod=mod@entry=0x555555f43b30, filename=filename@entry=0x7ffff74e8580, globals=globals@entry=0x7ffff746a4b0, locals=locals@entry=0x7ffff746a4b0,
flags=0x7fffffffc950, arena=arena@entry=0x7ffff74dad40, interactive_src=0x0, generate_new_source=0) at Python/pythonrun.c:1471
Found using cpython-review-toolkit with Claude Opus 4.6, using the /cpython-review-toolkit:explore Modules/_zoneinfo.c all deep command.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.15.0a7+ (heads/main:e0f7c1097e1, Mar 17 2026, 18:10:52) [Clang 21.1.2 (2ubuntu6)]
Linked PRs
- gh-146092: Fix error handling in _BINARY_OP_ADD_UNICODE opcode #146117
- [3.14] gh-146092: Fix error handling in _BINARY_OP_ADD_FLOAT opcode #146119
- gh-146092: Handle _PyFrame_GetFrameObject() failures properly #146124
- [3.14] gh-146092: Handle _PyFrame_GetFrameObject() failures properly (#146124) #146132
- [3.13] gh-146092: Handle _PyFrame_GetFrameObject() failures properly (GH-146124) (GH-146132) #146138
- gh-146092: Raise MemoryError on allocation failure in _zoneinfo #146165
- [3.14] gh-146092: Raise MemoryError on allocation failure in _zoneinfo (GH-146165) #146223
- [3.13] gh-146092: Raise MemoryError on allocation failure in _zoneinfo (GH-146165) #146224