diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2025-06-11 20:25:34 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2025-06-15 10:09:23 -0400 |
commit | def4a832425bfe94b96b8cb1146a83552b754fb4 (patch) | |
tree | 936a643c54a83a5dbed0a8a56b28ab066187b5fa | |
parent | a0fe051f8b2bcc919d67f822b674cdfe8cf1274b (diff) | |
download | bfs-def4a832425bfe94b96b8cb1146a83552b754fb4.tar.xz |
xspawn: Don't check xfaccess(..., X_OK) before fchdir()
Fixes: 8c130ca ("xspawn: Check X_OK even without $PATH resolution")
-rw-r--r-- | src/xspawn.c | 56 | ||||
-rw-r--r-- | tests/gnu/execdir_self.out | 1 | ||||
-rw-r--r-- | tests/gnu/execdir_self.sh | 9 |
3 files changed, 50 insertions, 16 deletions
diff --git a/src/xspawn.c b/src/xspawn.c index 3fa4e60..7fa16b7 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -401,18 +401,40 @@ static bool bfs_resolve_relative(const struct bfs_resolver *res) { return false; } +/** Check if the actions include fchdir(). */ +static bool bfs_spawn_will_chdir(const struct bfs_spawn *ctx) { + if (ctx) { + for_slist (const struct bfs_spawn_action, action, ctx) { + if (action->op == BFS_SPAWN_FCHDIR) { + return true; + } + } + } + + return false; +} + +/** Check if we can call xfaccessat() before file actions. */ +static bool bfs_can_access_early(const struct bfs_resolver *res, const struct bfs_spawn *ctx) { + if (res->exe[0] == '/') { + return true; + } + + if (bfs_spawn_will_chdir(ctx)) { + return false; + } + + return true; +} + /** Check if we can resolve the executable before file actions. */ static bool bfs_can_resolve_early(const struct bfs_resolver *res, const struct bfs_spawn *ctx) { if (!bfs_resolve_relative(res)) { return true; } - if (ctx) { - for_slist (const struct bfs_spawn_action, action, ctx) { - if (action->op == BFS_SPAWN_FCHDIR) { - return false; - } - } + if (bfs_spawn_will_chdir(ctx)) { + return false; } return true; @@ -442,17 +464,19 @@ static int bfs_resolve_early(struct bfs_resolver *res, const char *exe, const st }; if (bfs_can_skip_resolve(res, ctx)) { - // Do this check eagerly, even though posix_spawn()/execv() also - // would, because: - // - // - faccessat() is faster than fork()/clone() + execv() - // - posix_spawn() is not guaranteed to report ENOENT - if (xfaccessat(AT_FDCWD, exe, X_OK) == 0) { - res->done = true; - return 0; - } else { - return -1; + if (bfs_can_access_early(res, ctx)) { + // Do this check eagerly, even though posix_spawn()/execv() also + // would, because: + // + // - faccessat() is faster than fork()/clone() + execv() + // - posix_spawn() is not guaranteed to report ENOENT + if (xfaccessat(AT_FDCWD, exe, X_OK) != 0) { + return -1; + } } + + res->done = true; + return 0; } res->path = getenv("PATH"); diff --git a/tests/gnu/execdir_self.out b/tests/gnu/execdir_self.out new file mode 100644 index 0000000..3ad0640 --- /dev/null +++ b/tests/gnu/execdir_self.out @@ -0,0 +1 @@ +./bar.sh diff --git a/tests/gnu/execdir_self.sh b/tests/gnu/execdir_self.sh new file mode 100644 index 0000000..1fc5d04 --- /dev/null +++ b/tests/gnu/execdir_self.sh @@ -0,0 +1,9 @@ +cd "$TEST" +mkdir foo +cat >foo/bar.sh <<EOF +#!/bin/sh +printf '%s\n' "\$@" +EOF +chmod +x foo/bar.sh + +bfs_diff . -name bar.sh -execdir {} {} \; |