Skip to content

Report a missing executable on glibc older than 2.24 - #106

Open
bernardladenthin wants to merge 3 commits into
sheredom:mainfrom
bernardladenthin:fix/spawn-exec-errors-old-glibc
Open

Report a missing executable on glibc older than 2.24#106
bernardladenthin wants to merge 3 commits into
sheredom:mainfrom
bernardladenthin:fix/spawn-exec-errors-old-glibc

Conversation

@bernardladenthin

Copy link
Copy Markdown

Depends on #104 — without it the library does not build on any glibc old enough to show
this problem.

Problem

On glibc older than 2.24, posix_spawn does not report a failed exec back to the
caller. subprocess_create therefore returns success for an executable that does not
exist, and the child quietly exits with 127. Two tests in the suite cover exactly this
and fail on such systems:

create_ex_subprocess_create_failure_preserves_error
create_ex_subprocess_create_failure_does_not_leak_resources

Across the 9 language modes that is 18 failures on manylinux2014 (glibc 2.17). They only
became visible with #104, because before that the library did not build there at all.

The behaviour is glibc's, not this library's — a standalone probe with a missing binary:

glibc posix_spawn
2.17 rc=0, child exits 127
2.23 rc=0, child exits 127
2.24 rc=2 (ENOENT)
2.28 rc=2 (ENOENT)

Fix

Add a SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS probe alongside SUBPROCESS_HAVE_CWD, and
where it is 0, check the executable with access(..., X_OK) before spawning. errno and
the returned subprocess_error_e then match what every supported glibc produces.

Behaviour on glibc 2.24 and newer, and on every non-glibc platform, is unchanged — the
check is not compiled there at all.

Limitations

Only the explicit-path branch is covered. posix_spawnp resolves the executable
through PATH, which would mean reimplementing that search — including empty entries
meaning "current directory" and the ENOEXEC shell fallback. Measured on glibc 2.17 with
this patch applied:

posix_spawn  (explicit path): rc=-4  correct
posix_spawnp (PATH search)  : rc= 0  still unreported

Nobody is worse off than before, where both cases were silent, but the PATH case remains
wrong on those systems. Happy to extend it if you would rather have the full search.

There is a TOCTOU window between access and posix_spawn. On a platform whose
posix_spawn cannot report the failure at all, a best-effort check seemed better than
none — but it is a best-effort check, not a guarantee.

Testing

Environment libc Compiler Result
manylinux2014 glibc 2.17 GCC 10.2 423 pass (405 pass / 18 fail before)
AlmaLinux 8 glibc 2.28 clang 21.1 423 pass, unchanged
Debian 11 glibc 2.31 GCC 10.2 432 pass, unchanged
Ubuntu 24.04 glibc 2.39 GCC 13.3 432 pass, unchanged
Ubuntu 24.04 glibc 2.39 clang 18.1 432 pass, unchanged
Alpine 3.20 musl 1.2 GCC 13.2 432 pass, unchanged
Windows UCRT MSVC 19.44 387 pass, unchanged

AlmaLinux 8 is the interesting control: glibc 2.28 is old enough to lack
addchdir_np but new enough to report exec failures, so the probe must be 0 for one
feature and 1 for the other — and nothing there changes.

posix_spawn_file_actions_addchdir_np arrived in glibc 2.29, so
subprocess_create_ex fails to compile and link on older systems such as
manylinux2014. Add a SUBPROCESS_HAVE_CWD probe, report a requested cwd
as ENOSYS where the call is unavailable, and skip the cwd test there.
musl gained posix_spawn_file_actions_addchdir_np in 1.1.24 but exposes no
version macro, so the detection cannot cover older releases. Skip it when the
macro is already defined, letting those users set it themselves.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant