Bug report
Bug description:
I've been observing crashes when python code calls posix_spawn when being
run under gprofng.
As an example, I tested on a Fedora 42 x86_64 system with python3-3.13.13-1.fc42.x86_64
[Python 3.13.13 (main, Apr 8 2026, 00:00:00) [GCC 15.2.1 20260123 (Red Hat 15.2.1-7)] on linux]
and binutils-2.44-12.fc42.x86_64
[GNU gprofng binutils version 2.44]
Consider this script:
import os
os.posix_spawn("/usr/bin/echo", ["/usr/bin/echo", "world"], None)
print ("hello")
If I run this on its own, it works as expected:
$ python spawn.py
hello
world
But if I run it with gprofng, it crashes:
$ gprofng collect app python spawn.py
Creating experiment directory test.1.er (Process ID: 591620) ...
free(): invalid pointer
Aborted (core dumped)
$ world
The problem goes away if I change the third argument of the posix_spawn
command from None to os.environ. I also reproduce the crash with
cpython git main as of May 6 (65ed109).
The issue is that py_posix_spawn assumes that environ does not change
over the call to posix_spawn. However, when gprofng is used, it
interposes code wrapping the posix_spawn call that can modify the
environment (apparently to sanitize the environment from things like
LD_PRELOAD entries used by gprofng) and thus change environ.
In more detail, in py_posix_spawn, we have this before the spawn call:
EXECV_CHAR **envlist = NULL;
...
Py_ssize_t argc, envc;
...
if (env == Py_None) {
#ifdef USE_DARWIN_NS_GET_ENVIRON
environ = *_NSGetEnviron();
#endif
envlist = environ;
} else {
envlist = parse_envlist(env, &envc);
if (envlist == NULL) {
goto exit;
}
}
and after the call:
if (envlist && envlist != environ) {
free_string_array(envlist, envc);
}
So if the env argument is null and environ is changed by the call,
then we end up trying to free environ,
using a length taken from an uninitialized variable.
Here's one possible fix:
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 5bd53c2146a..4fef8a60647 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -7961,6 +7961,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a
environ = *_NSGetEnviron();
#endif
envlist = environ;
+ envc = (Py_ssize_t)-1;
} else {
envlist = parse_envlist(env, &envc);
if (envlist == NULL) {
@@ -8028,7 +8029,10 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a
if (attrp) {
(void)posix_spawnattr_destroy(attrp);
}
- if (envlist && envlist != environ) {
+ /* Can't just test for envlist != environ because some tools, such as
+ gprofng, interpose code around the posix_spawn call that can change
+ environ. */
+ if (envlist && envc != (Py_ssize_t)-1) {
free_string_array(envlist, envc);
}
if (argvlist) {
CPython versions tested on:
3.13, CPython main branch
Operating systems tested on:
Linux
Bug report
Bug description:
I've been observing crashes when python code calls posix_spawn when being
run under gprofng.
As an example, I tested on a Fedora 42 x86_64 system with python3-3.13.13-1.fc42.x86_64
[Python 3.13.13 (main, Apr 8 2026, 00:00:00) [GCC 15.2.1 20260123 (Red Hat 15.2.1-7)] on linux]
and binutils-2.44-12.fc42.x86_64
[GNU gprofng binutils version 2.44]
Consider this script:
If I run this on its own, it works as expected:
But if I run it with gprofng, it crashes:
The problem goes away if I change the third argument of the posix_spawn
command from
Nonetoos.environ. I also reproduce the crash withcpython git main as of May 6 (65ed109).
The issue is that py_posix_spawn assumes that environ does not change
over the call to posix_spawn. However, when gprofng is used, it
interposes code wrapping the posix_spawn call that can modify the
environment (apparently to sanitize the environment from things like
LD_PRELOAD entries used by gprofng) and thus change environ.
In more detail, in py_posix_spawn, we have this before the spawn call:
and after the call:
So if the
envargument is null andenvironis changed by the call,then we end up trying to free
environ,using a length taken from an uninitialized variable.
Here's one possible fix:
CPython versions tested on:
3.13, CPython main branch
Operating systems tested on:
Linux