Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork34k
Open
Description
What happened?
delattr(obj, name) andsetattr(obj, name) ends up inPyObject_SetAttr(..., value=NULL), which calls the generic setter that deletes fromobj.__dict__ via_PyDict_SetItem_LockHeld(value=NULL). Ifname is astr subclass with a re-entrant__hash__, hashingname runs user code that swaps out and frees the old__dict__. The deletion then continues using the stale dict pointer in_PyDict_DelItem_KnownHash_LockHeld, leading to a heap-use-after-free.
Proof of Concept:
PoC
classEvil(str):def__hash__(self):old=target.__dict__target.__dict__= {}delolddicts= []foriinrange(100):dicts.append({f"key{i}":f"value{i}"})return0classVictim:passtarget=Victim()delattr(target,Evil("marker"))
importgcclassEvil(str):def__hash__(self):old=target.__dict__target.__dict__= {}deloldgc.collect()return0classVictim:passtarget=Victim()setattr(target,Evil("marker"))
Related Code Snippet
Details
int_PyDict_SetItem_LockHeld(PyDictObject*dict,PyObject*name,PyObject*value){if (value==NULL) {// Bug: Trigger the override __hash__ method that free the dictPy_hash_thash=_PyObject_HashFast(name);if (hash==-1) {dict_unhashable_type(name);return-1; }// Access the freed dict buffer.return_PyDict_DelItem_KnownHash_LockHeld((PyObject*)dict,name,hash); }else {returnsetitem_lock_held(dict,name,value); }}int_PyDict_DelItem_KnownHash_LockHeld(PyObject*op,PyObject*key,Py_hash_thash){Py_ssize_tix;PyDictObject*mp;PyObject*old_value;if (!PyDict_Check(op)) {PyErr_BadInternalCall();return-1; }ASSERT_DICT_LOCKED(op);assert(key);assert(hash!=-1);mp= (PyDictObject*)op;// Leak the op/mp->me_key pointer backix=_Py_dict_lookup(mp,key,hash,&old_value);if (ix==DKIX_ERROR)return-1;if (ix==DKIX_EMPTY||old_value==NULL) {_PyErr_SetKeyError(key);return-1; }PyInterpreterState*interp=_PyInterpreterState_GET();_PyDict_NotifyEvent(interp,PyDict_EVENT_DELETED,mp,key,NULL);delitem_common(mp,hash,ix,old_value);return0;}
Affected Versions:
Details
| 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 Output:
Details
===================================================================1553427==ERROR: AddressSanitizer: heap-use-after-free on address 0x5080000a5748 at pc 0x63af6ffd3df9 bp 0x7fff69a92e30 sp 0x7fff69a92e20READ of size 8 at 0x5080000a5748 thread T0 #0 0x63af6ffd3df8 in _Py_TYPE Include/object.h:277 #1 0x63af6ffd3df8 in _PyDict_DelItem_KnownHash_LockHeld Objects/dictobject.c:2829 #2 0x63af6ffd4001 in _PyDict_SetItem_LockHeld Objects/dictobject.c:6914 #3 0x63af6ffd7cc4 in store_instance_attr_dict Objects/dictobject.c:7032 #4 0x63af6ffd7d56 in _PyObject_StoreInstanceAttribute Objects/dictobject.c:7053 #5 0x63af6fffe5e7 in _PyObject_GenericSetAttrWithDict Objects/object.c:1969 #6 0x63af6fffe85e in PyObject_GenericSetAttr Objects/object.c:2031 #7 0x63af6fffac11 in PyObject_SetAttr Objects/object.c:1476 #8 0x63af6fffb35a in PyObject_DelAttr Objects/object.c:1512 #9 0x63af7019c7c6 in builtin_delattr_impl Python/bltinmodule.c:1747 #10 0x63af7019c841 in builtin_delattr Python/clinic/bltinmodule.c.h:716 #11 0x63af6ffed364 in cfunction_vectorcall_FASTCALL Objects/methodobject.c:449 #12 0x63af6ff3ae7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #13 0x63af6ff3af72 in PyObject_Vectorcall Objects/call.c:327 #14 0x63af701b9056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #15 0x63af701fce54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #16 0x63af701fd148 in _PyEval_Vector Python/ceval.c:2001 #17 0x63af701fd3f8 in PyEval_EvalCode Python/ceval.c:884 #18 0x63af702f4507 in run_eval_code_obj Python/pythonrun.c:1365 #19 0x63af702f4723 in run_mod Python/pythonrun.c:1459 #20 0x63af702f557a in pyrun_file Python/pythonrun.c:1293 #21 0x63af702f8220 in _PyRun_SimpleFileObject Python/pythonrun.c:521 #22 0x63af702f84f6 in _PyRun_AnyFileObject Python/pythonrun.c:81 #23 0x63af7034974d in pymain_run_file_obj Modules/main.c:410 #24 0x63af703499b4 in pymain_run_file Modules/main.c:429 #25 0x63af7034b1b2 in pymain_run_python Modules/main.c:691 #26 0x63af7034b842 in Py_RunMain Modules/main.c:772 #27 0x63af7034ba2e in pymain_main Modules/main.c:802 #28 0x63af7034bdb3 in Py_BytesMain Modules/main.c:826 #29 0x63af6fdcf645 in main Programs/python.c:15 #30 0x73d687c2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #31 0x73d687c2a28a in __libc_start_main_impl ../csu/libc-start.c:360 #32 0x63af6fdcf574 in _start (/home/jackfromeast/Desktop/entropy/tasks/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: ff3dc40ea460bd4beb2c3a72283cca525b319bf0)0x5080000a5748 is located 40 bytes inside of 88-byte region [0x5080000a5720,0x5080000a5778)freed by thread T0 here: #0 0x73d6880fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52 #1 0x63af7000196d in _PyMem_RawFree Objects/obmalloc.c:91 #2 0x63af70003cd9 in _PyMem_DebugRawFree Objects/obmalloc.c:2955 #3 0x63af70003d1a in _PyMem_DebugFree Objects/obmalloc.c:3100 #4 0x63af7002c06c in PyObject_Free Objects/obmalloc.c:1522 #5 0x63af7026acf7 in PyObject_GC_Del Python/gc.c:2435 #6 0x63af6fffa4cc in free_object Objects/object.c:916 #7 0x63af6fff5e1e in clear_freelist Objects/object.c:902 #8 0x63af6fff71ba in _PyObject_ClearFreeLists Objects/object.c:933 #9 0x63af7026af8d in _PyGC_ClearAllFreeLists Python/gc_gil.c:14 #10 0x63af70268da2 in gc_collect_full Python/gc.c:1735 #11 0x63af7026a045 in _PyGC_Collect Python/gc.c:2096 #12 0x63af7034d0f0 in gc_collect_impl Modules/gcmodule.c:93 #13 0x63af7034d268 in gc_collect Modules/clinic/gcmodule.c.h:143 #14 0x63af701bda49 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:2361 #15 0x63af701fce54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #16 0x63af701fd148 in _PyEval_Vector Python/ceval.c:2001 #17 0x63af6ff3a9b8 in _PyFunction_Vectorcall Objects/call.c:413 #18 0x63af6ff3ae7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #19 0x63af6ff3b03f in PyObject_CallOneArg Objects/call.c:395 #20 0x63af7004d648 in call_unbound_noarg Objects/typeobject.c:3040 #21 0x63af70068fa0 in maybe_call_special_no_args Objects/typeobject.c:3153 #22 0x63af700695e4 in slot_tp_hash Objects/typeobject.c:10564 #23 0x63af6fff73d6 in PyObject_Hash Objects/object.c:1157 #24 0x63af6ffc4dda in _PyObject_HashFast Include/internal/pycore_object.h:872 #25 0x63af6ffd3fed in _PyDict_SetItem_LockHeld Objects/dictobject.c:6909 #26 0x63af6ffd7cc4 in store_instance_attr_dict Objects/dictobject.c:7032 #27 0x63af6ffd7d56 in _PyObject_StoreInstanceAttribute Objects/dictobject.c:7053 #28 0x63af6fffe5e7 in _PyObject_GenericSetAttrWithDict Objects/object.c:1969 #29 0x63af6fffe85e in PyObject_GenericSetAttr Objects/object.c:2031previously allocated by thread T0 here: #0 0x73d6880fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 #1 0x63af70002284 in _PyMem_RawMalloc Objects/obmalloc.c:63 #2 0x63af70001655 in _PyMem_DebugRawAlloc Objects/obmalloc.c:2887 #3 0x63af700016bd in _PyMem_DebugRawMalloc Objects/obmalloc.c:2920 #4 0x63af70002f3b in _PyMem_DebugMalloc Objects/obmalloc.c:3085 #5 0x63af7002bf28 in PyObject_Malloc Objects/obmalloc.c:1493 #6 0x63af7026a734 in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46 #7 0x63af7026a734 in gc_alloc Python/gc.c:2327 #8 0x63af7026a88a in _PyObject_GC_New Python/gc.c:2347 #9 0x63af6ffc8cad in new_dict Objects/dictobject.c:875 #10 0x63af6ffca396 in PyDict_New Objects/dictobject.c:973 #11 0x63af6ffca3e5 in dict_new_presized Objects/dictobject.c:2203 #12 0x63af6ffd2877 in _PyDict_FromItems Objects/dictobject.c:2244 #13 0x63af701b6e63 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1272 #14 0x63af701fce54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #15 0x63af701fd148 in _PyEval_Vector Python/ceval.c:2001 #16 0x63af6ff3a9b8 in _PyFunction_Vectorcall Objects/call.c:413 #17 0x63af6ff3ae7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #18 0x63af6ff3cbc0 in object_vacall Objects/call.c:819 #19 0x63af6ff3ce3e in PyObject_CallMethodObjArgs Objects/call.c:886 #20 0x63af70289cc9 in import_find_and_load Python/import.c:3701 #21 0x63af70290c01 in PyImport_ImportModuleLevelObject Python/import.c:3783 #22 0x63af701ac4a3 in _PyEval_ImportName Python/ceval.c:3017 #23 0x63af701d51b0 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:6219 #24 0x63af701fce54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #25 0x63af701fd148 in _PyEval_Vector Python/ceval.c:2001 #26 0x63af701fd3f8 in PyEval_EvalCode Python/ceval.c:884 #27 0x63af7019b94a in builtin_exec_impl Python/bltinmodule.c:1180 #28 0x63af7019bc4c in builtin_exec Python/clinic/bltinmodule.c.h:571 #29 0x63af6ffed123 in cfunction_vectorcall_FASTCALL_KEYWORDS Objects/methodobject.c:465 #30 0x63af6ff3e1d2 in _PyVectorcall_Call Objects/call.c:273SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:277 in _Py_TYPEShadow bytes around the buggy address: 0x5080000a5480: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 fa 0x5080000a5500: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 fa 0x5080000a5580: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 fa 0x5080000a5600: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00 0x5080000a5680: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa=>0x5080000a5700: fa fa fa fa fd fd fd fd fd[fd]fd fd fd fd fd fa 0x5080000a5780: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa 0x5080000a5800: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa 0x5080000a5880: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa 0x5080000a5900: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa 0x5080000a5980: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 02 faShadow 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==1553427==ABORTING