Skip to content

Commit def4a83

Browse files
committed
xspawn: Don't check xfaccess(..., X_OK) before fchdir()
Fixes: 8c130ca ("xspawn: Check X_OK even without $PATH resolution")
1 parent a0fe051 commit def4a83

File tree

3 files changed

+50
-16
lines changed

3 files changed

+50
-16
lines changed

src/xspawn.c

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -401,18 +401,40 @@ static bool bfs_resolve_relative(const struct bfs_resolver *res) {
401401
return false;
402402
}
403403

404+
/** Check if the actions include fchdir(). */
405+
static bool bfs_spawn_will_chdir(const struct bfs_spawn *ctx) {
406+
if (ctx) {
407+
for_slist (const struct bfs_spawn_action, action, ctx) {
408+
if (action->op == BFS_SPAWN_FCHDIR) {
409+
return true;
410+
}
411+
}
412+
}
413+
414+
return false;
415+
}
416+
417+
/** Check if we can call xfaccessat() before file actions. */
418+
static bool bfs_can_access_early(const struct bfs_resolver *res, const struct bfs_spawn *ctx) {
419+
if (res->exe[0] == '/') {
420+
return true;
421+
}
422+
423+
if (bfs_spawn_will_chdir(ctx)) {
424+
return false;
425+
}
426+
427+
return true;
428+
}
429+
404430
/** Check if we can resolve the executable before file actions. */
405431
static bool bfs_can_resolve_early(const struct bfs_resolver *res, const struct bfs_spawn *ctx) {
406432
if (!bfs_resolve_relative(res)) {
407433
return true;
408434
}
409435

410-
if (ctx) {
411-
for_slist (const struct bfs_spawn_action, action, ctx) {
412-
if (action->op == BFS_SPAWN_FCHDIR) {
413-
return false;
414-
}
415-
}
436+
if (bfs_spawn_will_chdir(ctx)) {
437+
return false;
416438
}
417439

418440
return true;
@@ -442,17 +464,19 @@ static int bfs_resolve_early(struct bfs_resolver *res, const char *exe, const st
442464
};
443465

444466
if (bfs_can_skip_resolve(res, ctx)) {
445-
// Do this check eagerly, even though posix_spawn()/execv() also
446-
// would, because:
447-
//
448-
// - faccessat() is faster than fork()/clone() + execv()
449-
// - posix_spawn() is not guaranteed to report ENOENT
450-
if (xfaccessat(AT_FDCWD, exe, X_OK) == 0) {
451-
res->done = true;
452-
return 0;
453-
} else {
454-
return -1;
467+
if (bfs_can_access_early(res, ctx)) {
468+
// Do this check eagerly, even though posix_spawn()/execv() also
469+
// would, because:
470+
//
471+
// - faccessat() is faster than fork()/clone() + execv()
472+
// - posix_spawn() is not guaranteed to report ENOENT
473+
if (xfaccessat(AT_FDCWD, exe, X_OK) != 0) {
474+
return -1;
475+
}
455476
}
477+
478+
res->done = true;
479+
return 0;
456480
}
457481

458482
res->path = getenv("PATH");

tests/gnu/execdir_self.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
./bar.sh

tests/gnu/execdir_self.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cd "$TEST"
2+
mkdir foo
3+
cat >foo/bar.sh <<EOF
4+
#!/bin/sh
5+
printf '%s\n' "\$@"
6+
EOF
7+
chmod +x foo/bar.sh
8+
9+
bfs_diff . -name bar.sh -execdir {} {} \;

0 commit comments

Comments
 (0)