Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
Open
Description
What happened?
A crafted integer subclass overrides__int__ so whenTextIOWrapper.seek converts the cookie withPyNumber_Long it detaches the wrapper and frees the buffer, yet the seek logic continues with the originalself->buffer, leading_io_TextIOWrapper_seek_impl to call a method on a NULL pointer and crash the interpreter.
Proof of Concept:
importiowrapper=io.TextIOWrapper(io.BytesIO(b"x"))classCookie(int):def__new__(cls,wrapper):obj=int.__new__(cls,0)obj.wrapper=wrapperreturnobjdef__int__(self):self.wrapper.detach()return0wrapper.seek(Cookie(wrapper),io.SEEK_SET)
Affected Versions:
| 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, 16:57:16) [Clang 18.1.3 (1ubuntu1)] | ASAN | 1 |
Vulnerable Code:
staticinttextiowrapper_parse_cookie(cookie_type*cookie,PyObject*cookieObj){unsignedcharbuffer[COOKIE_BUF_LEN];PyLongObject*cookieLong= (PyLongObject*)PyNumber_Long(cookieObj);if (cookieLong==NULL)return-1;if (_PyLong_AsByteArray(cookieLong,buffer,sizeof(buffer),PY_LITTLE_ENDIAN,0,1)<0) {Py_DECREF(cookieLong);return-1; }Py_DECREF(cookieLong);memcpy(&cookie->start_pos,buffer+OFF_START_POS,sizeof(cookie->start_pos));memcpy(&cookie->dec_flags,buffer+OFF_DEC_FLAGS,sizeof(cookie->dec_flags));memcpy(&cookie->bytes_to_feed,buffer+OFF_BYTES_TO_FEED,sizeof(cookie->bytes_to_feed));memcpy(&cookie->chars_to_skip,buffer+OFF_CHARS_TO_SKIP,sizeof(cookie->chars_to_skip));memcpy(&cookie->need_eof,buffer+OFF_NEED_EOF,sizeof(cookie->need_eof));return0;}staticPyObject*_io_TextIOWrapper_seek_impl(textio*self,PyObject*cookieObj,intwhence)/*[clinic end generated code: output=0a15679764e2d04d input=4bea78698be23d7e]*/{PyObject*posobj;cookie_typecookie;PyObject*res;intcmp;PyObject*snapshot;CHECK_ATTACHED(self);CHECK_CLOSED(self);Py_INCREF(cookieObj);if (!self->seekable) {_unsupported(self->state,"underlying stream is not seekable"); gotofail; }/* ... *//* The strategy of seek() is to go back to the safe start point * and replay the effect of read(chars_to_skip) from there. */if (textiowrapper_parse_cookie(&cookie,cookieObj)<0) gotofail;/* Seek back to the safe start point. */posobj=PyLong_FromOff_t(cookie.start_pos);if (posobj==NULL) gotofail;// self->buf has been set to nullres=PyObject_CallMethodOneArg(self->buffer,&_Py_ID(seek),posobj);
Sanitizer Output:
===================================================================1652913==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x5e52803ff39a bp 0x7fff1072a2c0 sp 0x7fff1072a200 T0)==1652913==The signal is caused by a READ memory access.==1652913==Hint: address points to the zero page. #0 0x5e52803ff39a in _Py_TYPE Include/object.h:277 #1 0x5e52803ff39a in _PyObject_GetMethodStackRef Objects/object.c:1698 #2 0x5e528033f796 in PyObject_VectorcallMethod Objects/call.c:840 #3 0x5e52807f39a2 in PyObject_CallMethodOneArg Include/cpython/abstract.h:74 #4 0x5e52807fb66b in _io_TextIOWrapper_seek_impl Modules/_io/textio.c:2647 #5 0x5e52807fbe97 in _io_TextIOWrapper_seek Modules/_io/clinic/textio.c.h:938 #6 0x5e528035d6ed in method_vectorcall_FASTCALL Objects/descrobject.c:402 #7 0x5e528033de7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #8 0x5e528033df72 in PyObject_Vectorcall Objects/call.c:327 #9 0x5e52805bc056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #10 0x5e52805ffe54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #11 0x5e5280600148 in _PyEval_Vector Python/ceval.c:2001 #12 0x5e52806003f8 in PyEval_EvalCode Python/ceval.c:884 #13 0x5e52806f7507 in run_eval_code_obj Python/pythonrun.c:1365 #14 0x5e52806f7723 in run_mod Python/pythonrun.c:1459 #15 0x5e52806f857a in pyrun_file Python/pythonrun.c:1293 #16 0x5e52806fb220 in _PyRun_SimpleFileObject Python/pythonrun.c:521 #17 0x5e52806fb4f6 in _PyRun_AnyFileObject Python/pythonrun.c:81 #18 0x5e528074c74d in pymain_run_file_obj Modules/main.c:410 #19 0x5e528074c9b4 in pymain_run_file Modules/main.c:429 #20 0x5e528074e1b2 in pymain_run_python Modules/main.c:691 #21 0x5e528074e842 in Py_RunMain Modules/main.c:772 #22 0x5e528074ea2e in pymain_main Modules/main.c:802 #23 0x5e528074edb3 in Py_BytesMain Modules/main.c:826 #24 0x5e52801d2645 in main Programs/python.c:15 #25 0x7642c742a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #26 0x7642c742a28a in __libc_start_main_impl ../csu/libc-start.c:360 #27 0x5e52801d2574 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: 202d5dbb945f6d5f5a66ad50e2688d56affd6ecb)AddressSanitizer can not provide additional info.SUMMARY: AddressSanitizer: SEGV Include/object.h:277 in _Py_TYPE==1652913==ABORTING