-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
Open
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Description
What happened?
get_weak_cache() assumes that _weak_cache is always owned by the object and safe to DECREF. When _weak_cache is a descriptor returning a new object, this assumption breaks, causing the cache object to be freed too early and leading to a use-after-free in zoneinfo_ZoneInfo_impl().
Proof of Concept:
from zoneinfo import ZoneInfo
class Cache:
def get(self, *args, **kwargs):
return None
def setdefault(self, *args, **kwargs):
return None
def clear(self, *args, **kwargs):
pass
class BombDescriptor:
def __get__(self, obj, owner):
return Cache()
class EvilZoneInfo(ZoneInfo):
pass
EvilZoneInfo._weak_cache = BombDescriptor()
EvilZoneInfo("UTC")Related Code Snippet
static PyObject *
zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
/*[clinic end generated code: output=95e61dab86bb95c3 input=ef73d7a83bf8790e]*/
{
zoneinfo_state *state = zoneinfo_get_state_by_self(type);
PyObject *instance = zone_from_strong_cache(state, type, key); // Trigged __eq__ method
if (instance != NULL || PyErr_Occurred()) {
return instance;
}
PyObject *weak_cache = get_weak_cache(state, type);
instance = PyObject_CallMethod(weak_cache, "get", "O", key, Py_None);
if (instance == NULL) {
return NULL;
}
if (instance == Py_None) {
Py_DECREF(instance);
PyObject *tmp = zoneinfo_new_instance(state, type, key);
if (tmp == NULL) {
return NULL;
}
instance =
PyObject_CallMethod(weak_cache, "setdefault", "OO", key, tmp);
Py_DECREF(tmp);
if (instance == NULL) {
return NULL;
}
((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE;
}
update_strong_cache(state, type, key, instance);
return instance;
}
static PyObject *
get_weak_cache(zoneinfo_state *state, PyTypeObject *type)
{
if (type == state->ZoneInfoType) {
return state->ZONEINFO_WEAK_CACHE;
}
else {
PyObject *cache =
PyObject_GetAttrString((PyObject *)type, "_weak_cache");
// BUG: The following DECREF free the newly assign cache and leads to UAF
// We are assuming that the type lives at least as long as the function
// that calls get_weak_cache, and that it holds a reference to the
// cache, so we'll return a "borrowed reference".
Py_XDECREF(cache);
return cache;
}
}
Affected Versions:
| Python Version | Status | Exit Code |
|---|---|---|
Python 3.9.24+ (heads/3.9:9c4638d, Oct 17 2025, 11:19:30) |
ASAN | 1 |
Python 3.10.19+ (heads/3.10:0142619, Oct 17 2025, 11:20:05) [GCC 13.3.0] |
ASAN | 1 |
Python 3.11.14+ (heads/3.11:88f3f5b, Oct 17 2025, 11:20:44) [GCC 13.3.0] |
ASAN | 1 |
Python 3.12.12+ (heads/3.12:8cb2092, Oct 17 2025, 11:21:35) [GCC 13.3.0] |
ASAN | 1 |
Python 3.13.9+ (heads/3.13:0760a57, Oct 17 2025, 11:22:25) [GCC 13.3.0] |
ASAN | 1 |
Python 3.14.0+ (heads/3.14:889e918, Oct 17 2025, 11:23:02) [GCC 13.3.0] |
ASAN | 1 |
Python 3.15.0a1+ (heads/main:fbf0843, Oct 17 2025, 11:23:37) [GCC 13.3.0] |
ASAN | 1 |
Sanitizer Report
=================================================================
==1448338==ERROR: AddressSanitizer: heap-use-after-free on address 0x513000037278 at pc 0x59e6317834ef bp 0x7ffc3a4859b0 sp 0x7ffc3a4859a0
READ of size 8 at 0x513000037278 thread T0
#0 0x59e6317834ee in _Py_TYPE Include/object.h:277
#1 0x59e6317834ee in PyObject_GetAttrString Objects/object.c:1178
#2 0x59e6316c3b2a in PyObject_CallMethod Objects/call.c:638
#3 0x7674c13914f2 in zoneinfo_ZoneInfo_impl Modules/_zoneinfo.c:329
#4 0x7674c1391710 in zoneinfo_ZoneInfo Modules/clinic/_zoneinfo.c.h:64
#5 0x59e6317e9346 in type_call Objects/typeobject.c:2448
#6 0x59e6316c2c71 in _PyObject_MakeTpCall Objects/call.c:242
#7 0x59e6316c2f19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167
#8 0x59e6316c2f72 in PyObject_Vectorcall Objects/call.c:327
#9 0x59e631941056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
#10 0x59e631984e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
#11 0x59e631985148 in _PyEval_Vector Python/ceval.c:2001
#12 0x59e6319853f8 in PyEval_EvalCode Python/ceval.c:884
#13 0x59e631a7c507 in run_eval_code_obj Python/pythonrun.c:1365
#14 0x59e631a7c723 in run_mod Python/pythonrun.c:1459
#15 0x59e631a7d57a in pyrun_file Python/pythonrun.c:1293
#16 0x59e631a80220 in _PyRun_SimpleFileObject Python/pythonrun.c:521
#17 0x59e631a804f6 in _PyRun_AnyFileObject Python/pythonrun.c:81
#18 0x59e631ad174d in pymain_run_file_obj Modules/main.c:410
#19 0x59e631ad19b4 in pymain_run_file Modules/main.c:429
#20 0x59e631ad31b2 in pymain_run_python Modules/main.c:691
#21 0x59e631ad3842 in Py_RunMain Modules/main.c:772
#22 0x59e631ad3a2e in pymain_main Modules/main.c:802
#23 0x59e631ad3db3 in Py_BytesMain Modules/main.c:826
#24 0x59e631557645 in main Programs/python.c:15
#25 0x7674c202a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#26 0x7674c202a28a in __libc_start_main_impl ../csu/libc-start.c:360
#27 0x59e631557574 in _start (/home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: ff3dc40ea460bd4beb2c3a72283cca525b319bf0)
0x513000037278 is located 56 bytes inside of 352-byte region [0x513000037240,0x5130000373a0)
freed by thread T0 here:
#0 0x7674c24fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
#1 0x59e63178996d in _PyMem_RawFree Objects/obmalloc.c:91
#2 0x59e63178bcd9 in _PyMem_DebugRawFree Objects/obmalloc.c:2955
#3 0x59e63178bd1a in _PyMem_DebugFree Objects/obmalloc.c:3100
#4 0x59e6317b406c in PyObject_Free Objects/obmalloc.c:1522
#5 0x59e6319f2cf7 in PyObject_GC_Del Python/gc.c:2435
#6 0x59e6317ce1cb in object_dealloc Objects/typeobject.c:7177
#7 0x59e6317ec663 in subtype_dealloc Objects/typeobject.c:2852
#8 0x59e631780481 in _Py_Dealloc Objects/object.c:3200
#9 0x7674c138967b in Py_DECREF Include/refcount.h:401
#10 0x7674c138969a in Py_XDECREF Include/refcount.h:511
#11 0x7674c138b07b in get_weak_cache Modules/_zoneinfo.c:303
#12 0x7674c13914ca in zoneinfo_ZoneInfo_impl Modules/_zoneinfo.c:328
#13 0x7674c1391710 in zoneinfo_ZoneInfo Modules/clinic/_zoneinfo.c.h:64
#14 0x59e6317e9346 in type_call Objects/typeobject.c:2448
#15 0x59e6316c2c71 in _PyObject_MakeTpCall Objects/call.c:242
#16 0x59e6316c2f19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167
#17 0x59e6316c2f72 in PyObject_Vectorcall Objects/call.c:327
#18 0x59e631941056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
#19 0x59e631984e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
#20 0x59e631985148 in _PyEval_Vector Python/ceval.c:2001
#21 0x59e6319853f8 in PyEval_EvalCode Python/ceval.c:884
#22 0x59e631a7c507 in run_eval_code_obj Python/pythonrun.c:1365
#23 0x59e631a7c723 in run_mod Python/pythonrun.c:1459
#24 0x59e631a7d57a in pyrun_file Python/pythonrun.c:1293
#25 0x59e631a80220 in _PyRun_SimpleFileObject Python/pythonrun.c:521
#26 0x59e631a804f6 in _PyRun_AnyFileObject Python/pythonrun.c:81
#27 0x59e631ad174d in pymain_run_file_obj Modules/main.c:410
#28 0x59e631ad19b4 in pymain_run_file Modules/main.c:429
#29 0x59e631ad31b2 in pymain_run_python Modules/main.c:691
previously allocated by thread T0 here:
#0 0x7674c24fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x59e63178a284 in _PyMem_RawMalloc Objects/obmalloc.c:63
#2 0x59e631789655 in _PyMem_DebugRawAlloc Objects/obmalloc.c:2887
#3 0x59e6317896bd in _PyMem_DebugRawMalloc Objects/obmalloc.c:2920
#4 0x59e63178af3b in _PyMem_DebugMalloc Objects/obmalloc.c:3085
#5 0x59e6317b3f28 in PyObject_Malloc Objects/obmalloc.c:1493
#6 0x59e6317e603b in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46
#7 0x59e6317e603b in _PyType_AllocNoTrack Objects/typeobject.c:2504
#8 0x59e6317e61c7 in PyType_GenericAlloc Objects/typeobject.c:2535
#9 0x59e6317de10e in object_new Objects/typeobject.c:7167
#10 0x59e6317e9346 in type_call Objects/typeobject.c:2448
#11 0x59e6316c2c71 in _PyObject_MakeTpCall Objects/call.c:242
#12 0x59e6316c2f19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167
#13 0x59e6316c2f72 in PyObject_Vectorcall Objects/call.c:327
#14 0x59e631941056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
#15 0x59e631984e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
#16 0x59e631985148 in _PyEval_Vector Python/ceval.c:2001
#17 0x59e6316c29b8 in _PyFunction_Vectorcall Objects/call.c:413
#18 0x59e6316c2e7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
#19 0x59e6316c2f72 in PyObject_Vectorcall Objects/call.c:327
#20 0x59e6317fbf01 in slot_tp_descr_get Objects/typeobject.c:10799
#21 0x59e6317fdb53 in _Py_type_getattro_impl Objects/typeobject.c:6377
#22 0x59e6317fdd37 in _Py_type_getattro Objects/typeobject.c:6419
#23 0x59e6317831cc in PyObject_GetAttr Objects/object.c:1313
#24 0x59e63178350e in PyObject_GetAttrString Objects/object.c:1183
#25 0x7674c138b070 in get_weak_cache Modules/_zoneinfo.c:299
#26 0x7674c13914ca in zoneinfo_ZoneInfo_impl Modules/_zoneinfo.c:328
#27 0x7674c1391710 in zoneinfo_ZoneInfo Modules/clinic/_zoneinfo.c.h:64
#28 0x59e6317e9346 in type_call Objects/typeobject.c:2448
#29 0x59e6316c2c71 in _PyObject_MakeTpCall Objects/call.c:242
#30 0x59e6316c2f19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167
SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:277 in _Py_TYPE
Shadow bytes around the buggy address:
0x513000036f80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x513000037000: fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa fa
0x513000037080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x513000037100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x513000037180: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa
=>0x513000037200: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd[fd]
0x513000037280: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x513000037300: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x513000037380: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa
0x513000037400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x513000037480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1448338==ABORTINGLinked PRs
Metadata
Metadata
Assignees
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Projects
Status
No status