-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
Description
Bug report
Bug description:
Version
3.10.13 (tags/v3.10.13:49965601d6, Nov 12 2023, 04:15:18)
bisect from commit 670e692,
Root cause
atexit_unregister dont’ consider counting reference and reentering other functions including atexit_unregister , atexit_clear .
It causes a free cb-func in the second loop while the first loop holds that reference.
This vulnerability caused by calling PyObject_RichCompareBool in c code, which call arbitrary user defined code like eq
static PyObject *
atexit_unregister(PyObject *module, PyObject *func)
{
struct atexit_state *state = get_atexit_state();
for (int i = 0; i < state->ncallbacks; i++)
{
atexit_py_callback *cb = state->callbacks[i];
if (cb == NULL) {
continue;
}
int eq = PyObject_RichCompareBool(cb->func, func, Py_EQ); //<--here
if (eq < 0) {
return NULL;
}
if (eq) {
atexit_delete_cb(state, i);
}
}
Py_RETURN_NONE;
}Poc
import atexit
class test(object):
def __eq__(self,o):
atexit._clear()
return NotImplemented
def __call__(self):
return
atexit.register(test())
atexit.unregister(test())Asan
asan
=75703==ERROR: AddressSanitizer: heap-use-after-free on address 0x607000644cb0 at pc 0x563a1a579158 bp 0x7f
feb4ffa460 sp 0x7ffeb4ffa450
READ of size 8 at 0x607000644cb0 thread T0
#0 0x563a1a579157 in _Py_INCREF Include/object.h:472
#1 0x563a1a579157 in _PyEval_MakeFrameVector Python/ceval.c:4826
#2 0x563a1a57a8b7 in _PyEval_Vector Python/ceval.c:5059
#3 0x563a1a3b8c9a in _PyFunction_Vectorcall Objects/call.c:342
#4 0x563a1a494550 in _PyObject_VectorcallTstate Include/cpython/abstract.h:114
#5 0x563a1a494550 in vectorcall_unbound Objects/typeobject.c:1629
#6 0x563a1a494550 in slot_tp_richcompare Objects/typeobject.c:7628
#7 0x563a1a440c51 in do_richcompare Objects/object.c:699
#8 0x563a1a440f93 in PyObject_RichCompare Objects/object.c:743
#9 0x563a1a4410a9 in PyObject_RichCompareBool Objects/object.c:765
#10 0x563a1a71618c in atexit_unregister Modules/atexitmodule.c:241
#11 0x563a1a7ef56d in cfunction_vectorcall_O Objects/methodobject.c:516
#12 0x563a1a548745 in _PyObject_VectorcallTstate Include/cpython/abstract.h:114
#13 0x563a1a551b8f in PyObject_Vectorcall Include/cpython/abstract.h:123
#14 0x563a1a551b8f in call_function Python/ceval.c:5893
#15 0x563a1a574978 in _PyEval_EvalFrameDefault Python/ceval.c:4181
#16 0x563a1a57a95d in _PyEval_EvalFrame Include/internal/pycore_ceval.h:46
#17 0x563a1a57a95d in _PyEval_Vector Python/ceval.c:5067
#18 0x563a1a57af25 in PyEval_EvalCode Python/ceval.c:1134
#19 0x563a1a60c907 in run_eval_code_obj Python/pythonrun.c:1291
#20 0x563a1a60d329 in run_mod Python/pythonrun.c:1312
#21 0x563a1a60d4c3 in pyrun_file Python/pythonrun.c:1208
#22 0x563a1a612b87 in _PyRun_SimpleFileObject Python/pythonrun.c:456
#23 0x563a1a612e80 in _PyRun_AnyFileObject Python/pythonrun.c:90
#24 0x563a1a3939b4 in pymain_run_file_obj Modules/main.c:353
#25 0x563a1a394184 in pymain_run_file Modules/main.c:372
#26 0x563a1a39668b in pymain_run_python Modules/main.c:587
#27 0x563a1a396809 in Py_RunMain Modules/main.c:666
#28 0x563a1a3969f9 in pymain_main Modules/main.c:696
#29 0x563a1a396d71 in Py_BytesMain Modules/main.c:720
#30 0x563a1a393245 in main Programs/python.c:15
#31 0x7f6c90d20d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#32 0x7f6c90d20e3f in __libc_start_main_impl ../csu/libc-start.c:392
#33 0x563a1a393174 in _start (/home/ubuntu/cpython/python+0x1fa174)
0x607000644cb0 is located 32 bytes inside of 72-byte region [0x607000644c90,0x607000644cd8)
freed by thread T0 here:
#0 0x7f6c910ba537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
#1 0x563a1a4460e8 in _PyMem_RawFree Objects/obmalloc.c:127
#2 0x563a1a446e01 in _PyMem_DebugRawFree Objects/obmalloc.c:2538
#3 0x563a1a4476e2 in _PyMem_DebugFree Objects/obmalloc.c:2671
#4 0x563a1a449273 in PyObject_Free Objects/obmalloc.c:709
#5 0x563a1a663cf0 in PyObject_GC_Del Modules/gcmodule.c:2375
#6 0x563a1a46a386 in object_dealloc Objects/typeobject.c:4510
#7 0x563a1a47da06 in subtype_dealloc Objects/typeobject.c:1460
#8 0x563a1a43ed59 in _Py_Dealloc Objects/object.c:2301
#9 0x563a1a7e0a89 in _Py_DECREF Include/object.h:500
#10 0x563a1a7e0a89 in frame_dealloc Objects/frameobject.c:591
#11 0x563a1a43ed59 in _Py_Dealloc Objects/object.c:2301
#12 0x563a1a57acc7 in _Py_DECREF Include/object.h:500
#13 0x563a1a57acc7 in _PyEval_Vector Python/ceval.c:5080
#14 0x563a1a3b8c9a in _PyFunction_Vectorcall Objects/call.c:342
#15 0x563a1a494550 in _PyObject_VectorcallTstate Include/cpython/abstract.h:114
#16 0x563a1a494550 in vectorcall_unbound Objects/typeobject.c:1629
#17 0x563a1a494550 in slot_tp_richcompare Objects/typeobject.c:7628
#18 0x563a1a440b52 in do_richcompare Objects/object.c:693
#19 0x563a1a440f93 in PyObject_RichCompare Objects/object.c:743
#20 0x563a1a4410a9 in PyObject_RichCompareBool Objects/object.c:765
#21 0x563a1a71618c in atexit_unregister Modules/atexitmodule.c:241
#22 0x563a1a7ef56d in cfunction_vectorcall_O Objects/methodobject.c:516
#23 0x563a1a548745 in _PyObject_VectorcallTstate Include/cpython/abstract.h:114
#24 0x563a1a551b8f in PyObject_Vectorcall Include/cpython/abstract.h:123
#25 0x563a1a551b8f in call_function Python/ceval.c:5893
#26 0x563a1a574978 in _PyEval_EvalFrameDefault Python/ceval.c:4181
#27 0x563a1a57a95d in _PyEval_EvalFrame Include/internal/pycore_ceval.h:46
#28 0x563a1a57a95d in _PyEval_Vector Python/ceval.c:5067
#29 0x563a1a57af25 in PyEval_EvalCode Python/ceval.c:1134
#30 0x563a1a60c907 in run_eval_code_obj Python/pythonrun.c:1291
#31 0x563a1a60d329 in run_mod Python/pythonrun.c:1312
#32 0x563a1a60d4c3 in pyrun_file Python/pythonrun.c:1208
#33 0x563a1a612b87 in _PyRun_SimpleFileObject Python/pythonrun.c:456
#34 0x563a1a612e80 in _PyRun_AnyFileObject Python/pythonrun.c:90
#35 0x563a1a3939b4 in pymain_run_file_obj Modules/main.c:353
previously allocated by thread T0 here:
#0 0x7f6c910ba887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
#1 0x563a1a446164 in _PyMem_RawMalloc Objects/obmalloc.c:99
#2 0x563a1a446c5c in _PyMem_DebugRawAlloc Objects/obmalloc.c:2471
#3 0x563a1a446cbb in _PyMem_DebugRawMalloc Objects/obmalloc.c:2504
#4 0x563a1a447724 in _PyMem_DebugMalloc Objects/obmalloc.c:2656
#5 0x563a1a4491ec in PyObject_Malloc Objects/obmalloc.c:685
#6 0x563a1a661abe in _PyObject_GC_Alloc Modules/gcmodule.c:2280
#7 0x563a1a663816 in _PyObject_GC_Malloc Modules/gcmodule.c:2307
#8 0x563a1a477585 in PyType_GenericAlloc Objects/typeobject.c:1156
#9 0x563a1a47b189 in object_new Objects/typeobject.c:4504
#10 0x563a1a48200c in type_call Objects/typeobject.c:1123
#11 0x563a1a3b9378 in _PyObject_MakeTpCall Objects/call.c:215
#12 0x563a1a548874 in _PyObject_VectorcallTstate Include/cpython/abstract.h:112
#13 0x563a1a551b8f in PyObject_Vectorcall Include/cpython/abstract.h:123
#14 0x563a1a551b8f in call_function Python/ceval.c:5893
#15 0x563a1a574b5d in _PyEval_EvalFrameDefault Python/ceval.c:4213
#16 0x563a1a57a95d in _PyEval_EvalFrame Include/internal/pycore_ceval.h:46
#17 0x563a1a57a95d in _PyEval_Vector Python/ceval.c:5067
#18 0x563a1a57af25 in PyEval_EvalCode Python/ceval.c:1134
#19 0x563a1a60c907 in run_eval_code_obj Python/pythonrun.c:1291
#20 0x563a1a60d329 in run_mod Python/pythonrun.c:1312
#21 0x563a1a60d4c3 in pyrun_file Python/pythonrun.c:1208
#22 0x563a1a612b87 in _PyRun_SimpleFileObject Python/pythonrun.c:456
#23 0x563a1a612e80 in _PyRun_AnyFileObject Python/pythonrun.c:90
#24 0x563a1a3939b4 in pymain_run_file_obj Modules/main.c:353
#25 0x563a1a394184 in pymain_run_file Modules/main.c:372
#26 0x563a1a39668b in pymain_run_python Modules/main.c:587
#27 0x563a1a396809 in Py_RunMain Modules/main.c:666
#28 0x563a1a3969f9 in pymain_main Modules/main.c:696
#29 0x563a1a396d71 in Py_BytesMain Modules/main.c:720
#30 0x563a1a393245 in main Programs/python.c:15
#31 0x7f6c90d20d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:5
SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:472 in _Py_INCREF
Shadow bytes around the buggy address:
0x0c0e800c0940: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
0x0c0e800c0950: fd fd fd fd fd fd fa fa fa fa fd fd fd fd fd fd
0x0c0e800c0960: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
0x0c0e800c0970: fd fd fa fa fa fa fd fd fd fd fd fd fd fd fd fd
0x0c0e800c0980: fa fa fa fa 00 00 00 00 00 00 00 00 00 fa fa fa
=>0x0c0e800c0990: fa fa fd fd fd fd[fd]fd fd fd fd fa fa fa fa fa
0x0c0e800c09a0: 00 00 00 00 00 00 00 00 00 02 fa fa fa fa fd fd
0x0c0e800c09b0: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
0x0c0e800c09c0: fd fd fd fd fd fd fa fa fa fa fd fd fd fd fd fd
0x0c0e800c09d0: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
0x0c0e800c09e0: fd fd fa fa fa fa fd fd fd fd fd fd fd fd fd fd
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
Shadow gap: cc
==75703==ABORTING
CPython versions tested on:
3.10
Operating systems tested on:
Linux