-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
Closed as duplicate of#112127
Closed as duplicate of#112127
Copy link
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?
atexit.unregister iterates its callback list while comparing each entry. A user-defined __eq__ that calls atexit._clear() frees the list during iteration, so the next comparison dereferences a stale pointer and reads freed memory.
Proof of Concept:
import atexit
class Callback:
def __call__(self):
pass
atexit.register(Callback())
class Evil:
def __eq__(self, other):
atexit._clear()
return NotImplemented
atexit.unregister(Evil())Affected Versions:
| Python Version | Status | Exit Code |
|---|---|---|
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 27 2025, 21:34:13) |
OK | 0 |
Python 3.10.19+ (heads/3.10:014261980b1, Oct 27 2025, 21:19:00) [Clang 18.1.3 (1ubuntu1)] |
OK | 0 |
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 27 2025, 21:20:35) [Clang 18.1.3 (1ubuntu1)] |
OK | 0 |
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 27 2025, 21:27:07) [Clang 18.1.3 (1ubuntu1)] |
OK | 0 |
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 27 2025, 21:28:49) [Clang 18.1.3 (1ubuntu1)] |
OK | 0 |
Python 3.14.0+ (heads/3.14:2e216728038, Oct 27 2025, 21:30:55) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 27 2025, 21:32:37) [Clang 18.1.3 (1ubuntu1)] |
ASAN | 1 |
Vulnerable Code:
static int
atexit_unregister_locked(PyObject *callbacks, PyObject *func)
{
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(callbacks); ++i) {
PyObject *tuple = PyList_GET_ITEM(callbacks, i);
assert(PyTuple_CheckExact(tuple));
PyObject *to_compare = PyTuple_GET_ITEM(tuple, 0);
int cmp = PyObject_RichCompareBool(func, to_compare, Py_EQ);
if (cmp < 0)
{
return -1;
}
if (cmp == 1) {
// We found a callback!
if (PyList_SetSlice(callbacks, i, i + 1, NULL) < 0) {
return -1;
}
--i;
}
}
return 0;
}Sanitizer Output:
=================================================================
==1088062==ERROR: AddressSanitizer: heap-use-after-free on address 0x513000025df8 at pc 0x6546cb291883 bp 0x7ffdb2707160 sp 0x7ffdb2707150
READ of size 8 at 0x513000025df8 thread T0
#0 0x6546cb291882 in _Py_TYPE Include/object.h:277
#1 0x6546cb291882 in do_richcompare Objects/object.c:1064
#2 0x6546cb29193d in PyObject_RichCompare Objects/object.c:1108
#3 0x6546cb2919ad in PyObject_RichCompareBool Objects/object.c:1130
#4 0x6546cb5f50ad in atexit_unregister_locked Modules/atexitmodule.c:262
#5 0x6546cb5f5130 in atexit_unregister Modules/atexitmodule.c:294
#6 0x6546cb2843fe in cfunction_vectorcall_O Objects/methodobject.c:536
#7 0x6546cb1d1e7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
#8 0x6546cb1d1f72 in PyObject_Vectorcall Objects/call.c:327
#9 0x6546cb450056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
#10 0x6546cb493e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
#11 0x6546cb494148 in _PyEval_Vector Python/ceval.c:2001
#12 0x6546cb4943f8 in PyEval_EvalCode Python/ceval.c:884
#13 0x6546cb58b507 in run_eval_code_obj Python/pythonrun.c:1365
#14 0x6546cb58b723 in run_mod Python/pythonrun.c:1459
#15 0x6546cb58c57a in pyrun_file Python/pythonrun.c:1293
#16 0x6546cb58f220 in _PyRun_SimpleFileObject Python/pythonrun.c:521
#17 0x6546cb58f4f6 in _PyRun_AnyFileObject Python/pythonrun.c:81
#18 0x6546cb5e074d in pymain_run_file_obj Modules/main.c:410
#19 0x6546cb5e09b4 in pymain_run_file Modules/main.c:429
#20 0x6546cb5e21b2 in pymain_run_python Modules/main.c:691
#21 0x6546cb5e2842 in Py_RunMain Modules/main.c:772
#22 0x6546cb5e2a2e in pymain_main Modules/main.c:802
#23 0x6546cb5e2db3 in Py_BytesMain Modules/main.c:826
#24 0x6546cb066645 in main Programs/python.c:15
#25 0x7fdd5822a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#26 0x7fdd5822a28a in __libc_start_main_impl ../csu/libc-start.c:360
#27 0x6546cb066574 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: 202d5dbb945f6d5f5a66ad50e2688d56affd6ecb)
0x513000025df8 is located 56 bytes inside of 352-byte region [0x513000025dc0,0x513000025f20)
freed by thread T0 here:
#0 0x7fdd586fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
#1 0x6546cb29896d in _PyMem_RawFree Objects/obmalloc.c:91
#2 0x6546cb29acd9 in _PyMem_DebugRawFree Objects/obmalloc.c:2955
#3 0x6546cb29ad1a in _PyMem_DebugFree Objects/obmalloc.c:3100
#4 0x6546cb2c306c in PyObject_Free Objects/obmalloc.c:1522
#5 0x6546cb501cf7 in PyObject_GC_Del Python/gc.c:2435
#6 0x6546cb2dd1cb in object_dealloc Objects/typeobject.c:7177
#7 0x6546cb2fb663 in subtype_dealloc Objects/typeobject.c:2852
#8 0x6546cb28f481 in _Py_Dealloc Objects/object.c:3200
#9 0x6546cb4f792f in Py_DECREF_MORTAL Include/internal/pycore_object.h:450
#10 0x6546cb4f79e5 in PyStackRef_XCLOSE Include/internal/pycore_stackref.h:736
#11 0x6546cb4f8a86 in _PyFrame_ClearLocals Python/frame.c:101
#12 0x6546cb4f8c7e in _PyFrame_ClearExceptCode Python/frame.c:126
#13 0x6546cb43cbcd in clear_thread_frame Python/ceval.c:1826
#14 0x6546cb440c31 in _PyEval_FrameClearAndPop Python/ceval.c:1850
#15 0x6546cb4874c9 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:10403
#16 0x6546cb493e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
#17 0x6546cb494148 in _PyEval_Vector Python/ceval.c:2001
#18 0x6546cb1d19b8 in _PyFunction_Vectorcall Objects/call.c:413
#19 0x6546cb2e456b in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
#20 0x6546cb3009a6 in vectorcall_unbound Objects/typeobject.c:3033
#21 0x6546cb3009a6 in maybe_call_special_one_arg Objects/typeobject.c:3175
#22 0x6546cb300ad3 in _PyObject_MaybeCallSpecialOneArg Objects/typeobject.c:3190
#23 0x6546cb300b19 in slot_tp_richcompare Objects/typeobject.c:10729
#24 0x6546cb291687 in do_richcompare Objects/object.c:1059
#25 0x6546cb29193d in PyObject_RichCompare Objects/object.c:1108
#26 0x6546cb2919ad in PyObject_RichCompareBool Objects/object.c:1130
#27 0x6546cb5f50ad in atexit_unregister_locked Modules/atexitmodule.c:262
#28 0x6546cb5f5130 in atexit_unregister Modules/atexitmodule.c:294
#29 0x6546cb2843fe in cfunction_vectorcall_O Objects/methodobject.c:536
#30 0x6546cb1d1e7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169
previously allocated by thread T0 here:
#0 0x7fdd586fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
#1 0x6546cb299284 in _PyMem_RawMalloc Objects/obmalloc.c:63
#2 0x6546cb298655 in _PyMem_DebugRawAlloc Objects/obmalloc.c:2887
#3 0x6546cb2986bd in _PyMem_DebugRawMalloc Objects/obmalloc.c:2920
#4 0x6546cb299f3b in _PyMem_DebugMalloc Objects/obmalloc.c:3085
#5 0x6546cb2c2f28 in PyObject_Malloc Objects/obmalloc.c:1493
#6 0x6546cb2f503b in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46
#7 0x6546cb2f503b in _PyType_AllocNoTrack Objects/typeobject.c:2504
#8 0x6546cb2f51c7 in PyType_GenericAlloc Objects/typeobject.c:2535
#9 0x6546cb2ed10e in object_new Objects/typeobject.c:7167
#10 0x6546cb2f8346 in type_call Objects/typeobject.c:2448
#11 0x6546cb1d1c71 in _PyObject_MakeTpCall Objects/call.c:242
#12 0x6546cb1d1f19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167
#13 0x6546cb1d1f72 in PyObject_Vectorcall Objects/call.c:327
#14 0x6546cb450056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620
#15 0x6546cb493e54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121
#16 0x6546cb494148 in _PyEval_Vector Python/ceval.c:2001
#17 0x6546cb4943f8 in PyEval_EvalCode Python/ceval.c:884
#18 0x6546cb58b507 in run_eval_code_obj Python/pythonrun.c:1365
#19 0x6546cb58b723 in run_mod Python/pythonrun.c:1459
#20 0x6546cb58c57a in pyrun_file Python/pythonrun.c:1293
#21 0x6546cb58f220 in _PyRun_SimpleFileObject Python/pythonrun.c:521
#22 0x6546cb58f4f6 in _PyRun_AnyFileObject Python/pythonrun.c:81
#23 0x6546cb5e074d in pymain_run_file_obj Modules/main.c:410
#24 0x6546cb5e09b4 in pymain_run_file Modules/main.c:429
#25 0x6546cb5e21b2 in pymain_run_python Modules/main.c:691
#26 0x6546cb5e2842 in Py_RunMain Modules/main.c:772
#27 0x6546cb5e2a2e in pymain_main Modules/main.c:802
#28 0x6546cb5e2db3 in Py_BytesMain Modules/main.c:826
#29 0x6546cb066645 in main Programs/python.c:15
#30 0x7fdd5822a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:277 in _Py_TYPE
Shadow bytes around the buggy address:
0x513000025b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x513000025b80: fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa fa
0x513000025c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x513000025c80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x513000025d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x513000025d80: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd[fd]
0x513000025e00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x513000025e80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x513000025f00: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa
0x513000025f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x513000026000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
==1088062==ABORTING
yihong0618
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