diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2025-06-15 12:33:38 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2025-06-15 12:35:40 -0400 |
commit | 134aa5d1c0bc71ec6cbf8f18f803976d6b60b0cc (patch) | |
tree | ff45374e19339f720ff5058413134ab1e2970a8e | |
parent | 38f3e3e7ba210bbc45c2077102c308ed8abae061 (diff) | |
download | bfs-134aa5d1c0bc71ec6cbf8f18f803976d6b60b0cc.tar.xz |
xspawn: Work around a NetBSD posix_spawn() bug
NetBSD's posix_spawn() surprisingly has the same bug as its
posix_spawnp(): the executable is resolved before the file actions.
Detect this case and work around it by falling back to fork()/exec() if
we need to.
-rw-r--r-- | src/xspawn.c | 11 | ||||
-rw-r--r-- | tests/xspawn.c | 31 |
2 files changed, 29 insertions, 13 deletions
diff --git a/src/xspawn.c b/src/xspawn.c index 7fa16b7..7ead45a 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -232,6 +232,11 @@ int bfs_spawn_adddup2(struct bfs_spawn *ctx, int oldfd, int newfd) { */ #define BFS_POSIX_SPAWNP_AFTER_FCHDIR !(__APPLE__ || __NetBSD__) +/** + * NetBSD even resolves the executable before file actions with posix_spawn()! + */ +#define BFS_POSIX_SPAWN_AFTER_FCHDIR !__NetBSD__ + int bfs_spawn_addfchdir(struct bfs_spawn *ctx, int fd) { struct bfs_spawn_action *action = bfs_spawn_action(BFS_SPAWN_FCHDIR); if (!action) { @@ -553,6 +558,12 @@ static bool bfs_use_posix_spawn(const struct bfs_resolver *res, const struct bfs } #endif +#if !BFS_POSIX_SPAWN_AFTER_FCHDIR + if (res->exe[0] != '/' && bfs_spawn_will_chdir(ctx)) { + return false; + } +#endif + return true; } diff --git a/tests/xspawn.c b/tests/xspawn.c index 0244006..6864192 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -99,6 +99,22 @@ static int reset_path(char *old_path) { return ret; } +/** Spawn the test binary and check for success. */ +static void check_spawnee(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp) { + pid_t pid = bfs_spawn(exe, ctx, argv, envp); + if (!bfs_echeck(pid >= 0, "bfs_spawn('%s')", exe)) { + return; + } + + int wstatus; + bool exited = bfs_echeck(xwaitpid(pid, &wstatus, 0) == pid) + && bfs_check(WIFEXITED(wstatus)); + if (exited) { + int wexit = WEXITSTATUS(wstatus); + bfs_check(wexit == EXIT_SUCCESS, "xspawnee: exit(%d)", wexit); + } +} + /** Check that we resolve executables in $PATH correctly. */ static void check_use_path(bool use_posix) { struct bfs_spawn spawn; @@ -133,20 +149,9 @@ static void check_use_path(bool use_posix) { } char *argv[] = {"xspawnee", old_path, NULL}; - pid_t pid = bfs_spawn("xspawnee", &spawn, argv, envp); - if (!bfs_echeck(pid >= 0, "bfs_spawn()")) { - goto path; - } - - int wstatus; - bool exited = bfs_echeck(xwaitpid(pid, &wstatus, 0) == pid) - && bfs_check(WIFEXITED(wstatus)); - if (exited) { - int wexit = WEXITSTATUS(wstatus); - bfs_check(wexit == EXIT_SUCCESS, "xspawnee: exit(%d)", wexit); - } + check_spawnee("xspawnee", &spawn, argv, envp); + check_spawnee("tests/xspawnee", &spawn, argv, envp); -path: bfs_echeck(reset_path(old_path) == 0); env: for (char **var = envp; *var; ++var) { |