Skip to content

os.exec*e / os.spawn*e with env crash on Windows non-MSVC builds (MINGW/clang) #158373

Description

@cubershipyard

Bug

On Windows builds compiled with a non-MSVC toolchain (MINGW / clang, as shipped
by MSYS2), the os.exec*e and os.spawn*e variants crash with an access
violation whenever an env argument is passed. The non-env variants are fine.

This is the same defect as gh-67650 ("All os.exec*e variants crash on Windows",
formerly bpo-23462); it was closed as fixed in 3.6, but the fix does not cover
MINGW/clang builds, which still crash.

Reproducer

import os, sys
exe = os.path.join(os.path.dirname(sys.executable), "python.exe")
os.execve(exe, [exe, "-c", "pass"], {"PATH": os.environ["PATH"]})
$ python repro.py
Windows fatal exception: access violation
Current thread 0x000045cc (most recent call first):
  File "repro.py", line 4 in <module>
$ echo $?
139

Measured (MINGW clang64 python 3.14.7, 20 runs each)

Call Success
os.execv(exe, argv) (no env) 20/20
os.spawnv(P_WAIT, exe, argv) (no env) 20/20
os.execve(exe, argv, env) 0/20
os.spawnve(P_WAIT, exe, argv, env) 0/20

Root cause

HAVE_WEXECV / HAVE_WSPAWNV are defined in Modules/posixmodule.c only for
the Microsoft compiler:

#elif defined( _MSC_VER)
  /* Microsoft compiler */
  ...
#  if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)
#    define HAVE_WEXECV     1
#    define HAVE_WSPAWNV    1
#  endif
#endif

MINGW/clang does not define _MSC_VER, so HAVE_WEXECV is absent and the code
falls into the narrow branch. On Windows path->narrow is NULL (the path is
converted to wide), so the call faults (Modules/posixmodule.c, v3.14.0):

/* execv, line 7024 */
#ifdef HAVE_WEXECV
    _wexecv(path->wide, argvlist);
#else
    execv(path->narrow, argvlist);          /* path->narrow == NULL -> crash */
#endif

/* execve, line 7112 */
#ifdef HAVE_WEXECV
        _wexecve(path->wide, argvlist, envlist);
#else
        execve(path->narrow, argvlist, envlist);   /* crash */
#endif

Evidence the wide functions are available

MSYS2 pyconfig.h has HAVE_EXECV but not HAVE_WEXECV:

$ grep -n "WEXECV\|HAVE_EXECV" /clang64/include/python3.14/pyconfig.h
364:#define HAVE_EXECV 1          # <- no HAVE_WEXECV

Yet msvcrt exports all four wide calls:

>>> import ctypes
>>> for n in ("_wexecv", "_wexecve", "_wspawnv", "_wspawnve"):
...     print(n, hasattr(ctypes.CDLL("msvcrt.dll"), n))
_wexecv True
_wexecve True
_wspawnv True
_wspawnve True

So the CRT supports the fix; only the compile-time gate is wrong for this
toolchain.

Suggested fix

Detect the wide CRT functions for non-MSVC Windows toolchains as well, instead
of gating HAVE_WEXECV / HAVE_WSPAWNV on _MSC_VER. If detection is awkward
in pyconfig, posixmodule.c could define them for MS_WINDOWS builds whose
CRT provides _wexecv/_wexecve/_wspawnv/_wspawnve.

Impact

Hard crash for any MINGW/clang Windows Python (MSYS2 clang64/ucrt64/mingw64)
code that uses os.execve/execvpe/spawnve. No pure-Python workaround for
exec*e (they replace the process); subprocess with env is a workaround.

Related: the same issue was reported downstream to MSYS2 —
msys2/MINGW-packages#32022

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions