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

    OS-unsupportedOS-windowsextension-modulesC modules in the Modules dirpendingThe issue will be closed if no feedback is providedtype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions