-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
Open
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Description
What happened?
In parse_envlist the borrowed entries from PyMapping_Keys and PyMapping_Values are processed by PyUnicode_FSConverter, which triggers user __fspath__ on PathEntry objects. The crafted AliasEnv drops each entry during conversion so the loop keeps a dangling pointer, and the subsequent PyOS_FSPath access reuses freed memory leading to a use-after-free.
Proof of Concept:
import os
victim_list = []
class ExploitPath:
def __fspath__(self):
victim_list.clear()
return b"pwn"
class EvilEnv:
def __len__(self): return 1
def keys(self): return victim_list
def values(self): return victim_list
def __getitem__(self, key): return 1
victim_list.append(ExploitPath())
try:
os.execve("/bin/sh", ["sh"], EvilEnv())
except OSError:
passAffected 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 */
static PyObject *
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)
goto fail_0;
/* ... */
return NULL;
}
static EXECV_CHAR**
parse_envlist(PyObject* env, Py_ssize_t *envc_ptr)
{
Py_ssize_t i, pos, envc;
PyObject *keys=NULL, *vals=NULL;
PyObject *key2, *val2, *keyval;
EXECV_CHAR **envlist;
i = PyMapping_Size(env);
if (i < 0)
return NULL;
envlist = PyMem_NEW(EXECV_CHAR *, i + 1);
if (envlist == NULL) {
PyErr_NoMemory();
return NULL;
}
envc = 0;
keys = PyMapping_Keys(env);
if (!keys)
goto error;
vals = PyMapping_Values(env);
if (!vals)
goto error;
if (!PyList_Check(keys) || !PyList_Check(vals)) {
PyErr_Format(PyExc_TypeError,
"env.keys() or env.values() is not a list");
goto error;
}
for (pos = 0; pos < i; pos++) {
PyObject *key = PyList_GetItem(keys, pos); /* crashing pointer derived */
if (key == NULL) {
goto error;
}
PyObject *val = PyList_GetItem(vals, pos);
if (val == NULL) {
goto error;
}
#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV)
if (!PyUnicode_FSDecoder(key, &key2))
goto error;
if (!PyUnicode_FSDecoder(val, &val2)) {
Py_DECREF(key2);
goto error;
}
/* 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);
goto error;
}
keyval = PyUnicode_FromFormat("%U=%U", key2, val2);
#else
if (!PyUnicode_FSConverter(key, &key2)) /* Reentrant call site */
goto error;
if (!PyUnicode_FSConverter(val, &val2)) {
Py_DECREF(key2);
goto error;
}
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);
goto error;
}
keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2),
PyBytes_AS_STRING(val2));
#endif
Py_DECREF(key2);
Py_DECREF(val2);
if (!keyval)
goto error;
if (!fsconvert_strdup(keyval, &envlist[envc++])) {
Py_DECREF(keyval);
goto error;
}
Py_DECREF(keyval);
}
Py_DECREF(vals);
Py_DECREF(keys);
envlist[envc] = 0;
*envc_ptr = envc;
return envlist;
error:
Py_XDECREF(keys);
Py_XDECREF(vals);
free_string_array(envlist, envc);
return NULL;
}
PyObject *
PyOS_FSPath(PyObject *path)
{
if (PyUnicode_Check(path) || PyBytes_Check(path)) { /* Crash site */
return Py_NewRef(path);
}
/* ... */
return path_repr;
}
/* Clobbering Path */
static void
list_clear_impl(PyListObject *a, bool is_resize)
{
PyObject **items = a->ob_item;
/* Because XDECREF can recursively invoke operations on this list,
we make it empty first. */
Py_ssize_t i = 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 0x7fff44ef1d40
READ 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:521
previously 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:58
SUMMARY: AddressSanitizer: heap-use-after-free Include/object.h:277 in _Py_TYPE
Shadow 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 00
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
==1945479==ABORTING
Linked PRs
Metadata
Metadata
Assignees
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump