Skip to content

fix(File): getExecutablePath skips directories shadowing executables …#5211

Merged
aleks-f merged 3 commits intomainfrom
5210-get-exec-path
Feb 20, 2026
Merged

fix(File): getExecutablePath skips directories shadowing executables …#5211
aleks-f merged 3 commits intomainfrom
5210-get-exec-path

Conversation

@aleks-f
Copy link
Copy Markdown
Member

@aleks-f aleks-f commented Feb 15, 2026

…on PATH #5210

A directory in CWD with the same name as a PATH executable (e.g. a source checkout named crun/ shadowing /usr/bin/crun) caused getExecutablePathImpl() to return empty instead of finding the real executable. The root cause was findInPath() returning the first existing entry (including directories) with no way to continue searching. Replaced the findInPath() call in the bare-name branch with an inline CWD + PATH search requiring S_ISREG.

Added testGetExecutablePathDirectoryShadow (FileTest) and testPathResolution (ProcessRunnerTest).

…on PATH #5210

A directory in CWD with the same name as a PATH executable (e.g. a
source checkout named crun/ shadowing /usr/bin/crun) caused
getExecutablePathImpl() to return empty instead of finding the real
executable. The root cause was findInPath() returning the first
existing entry (including directories) with no way to continue
searching. Replaced the findInPath() call in the bare-name branch
with an inline CWD + PATH search requiring S_ISREG.

Added testGetExecutablePathDirectoryShadow (FileTest) and
testPathResolution (ProcessRunnerTest).
@aleks-f aleks-f added this to the Release 1.15.1 milestone Feb 15, 2026
@aleks-f aleks-f self-assigned this Feb 15, 2026
@aleks-f aleks-f added the bug label Feb 15, 2026
@aleks-f aleks-f linked an issue Feb 15, 2026 that may be closed by this pull request
@aleks-f aleks-f requested a review from Copilot February 15, 2026 10:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where a directory in the current working directory with the same name as a PATH executable would incorrectly shadow the real executable, causing getExecutablePath() to return empty. The fix replaces the findInPath() call with an inline search that explicitly checks for regular executable files using S_ISREG.

Changes:

  • Modified File_UNIX.cpp to implement inline CWD + PATH search with explicit regular file checking
  • Added testGetExecutablePathDirectoryShadow test in FileTest to verify directory shadowing is prevented
  • Added testPathResolution test in ProcessRunnerTest to verify end-to-end process execution with directory shadowing

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Foundation/src/File_UNIX.cpp Replaces findInPath() with inline search requiring S_ISREG to skip directories
Foundation/testsuite/src/FileTest.h Declares new test method testGetExecutablePathDirectoryShadow
Foundation/testsuite/src/FileTest.cpp Implements test verifying directory shadowing doesn't prevent executable resolution
Foundation/testsuite/src/ProcessRunnerTest.h Declares new test method testPathResolution
Foundation/testsuite/src/ProcessRunnerTest.cpp Implements end-to-end test with ProcessRunner verifying directory shadowing fix

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@matejk matejk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Notes

1. Code duplication with canExecuteImpl()

The isExecutableFile lambda in getExecutablePathImpl() (File_UNIX.cpp:115-126) duplicates the exact logic of canExecuteImpl() (lines 205-219). If the permission-check logic ever changes (e.g., adding supplementary group support), it would need to be updated in two places. Consider extracting a shared static helper or calling canExecuteImpl() from the lambda to avoid divergence.

2. Root user check — only checks S_IXUSR

For root (geteuid() == 0), the code only checks S_IXUSR. The kernel allows root to execute a file if any execute bit is set (S_IXUSR | S_IXGRP | S_IXOTH). This means a file with mode ---x--x--x owned by another user would be rejected by this code but accepted by the kernel. This is a pre-existing pattern (same in canExecuteImpl), but worth noting.

3. PATH cache bypass — consider using getPathDirectories()

File.cpp already has a thread-safe cached PATH tokenizer getPathDirectories() (File.cpp:47-76) that avoids re-reading and re-tokenizing PATH on every call. This fix duplicates that logic and re-parses PATH from scratch each time.

Since File_UNIX.cpp is #included from File.cpp (line 34), getPathDirectories() could be made accessible by moving its definition (and the anonymous namespace) above the platform #include. This would eliminate the duplicated PATH parsing and provide the same caching benefit that findInPath() already enjoys.

@aleks-f aleks-f merged commit 3c6c4fa into main Feb 20, 2026
102 checks passed
@aleks-f aleks-f deleted the 5210-get-exec-path branch February 20, 2026 09:08
matejk pushed a commit that referenced this pull request Feb 20, 2026
#5211)

* fix(File): getExecutablePath skips directories shadowing executables on PATH #5210

A directory in CWD with the same name as a PATH executable (e.g. a
source checkout named crun/ shadowing /usr/bin/crun) caused
getExecutablePathImpl() to return empty instead of finding the real
executable. The root cause was findInPath() returning the first
existing entry (including directories) with no way to continue
searching. Replaced the findInPath() call in the bare-name branch
with an inline CWD + PATH search requiring S_ISREG.

Added testGetExecutablePathDirectoryShadow (FileTest) and
testPathResolution (ProcessRunnerTest).

* fix(ProcessRunnerTest): wrong cmdDir

* fix(File): code duplication; root can execute any x bit #5210
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

File::getExecutablePath()` returns directory instead of executable

3 participants