Skip to content

Conversation

@abendrothj
Copy link
Contributor

This PR fixes a TOCTOU (Time-of-Check-Time-of-Use) race condition in the install -D command where an attacker could replace a directory component with a symlink between directory creation and file installation, redirecting writes to an arbitrary location.

Changes

  • Added mkdir_at() and open_file_at() methods to DirFd for safe operations
  • Added open_no_follow() to prevent following symlinks when opening directories
  • Added create_dir_all_safe() function that uses directory file descriptors
  • Modified install -D to use safe traversal functions instead of pathname-based ops
  • Added copy_file_safe() function for safe file copying using directory fds
  • Added tests to verify the fix prevents symlink race conditions

How the Fix Works

The fix prevents the race condition by:

  1. Using directory file descriptors (mkdirat/openat) instead of pathnames
  2. Keeping directory fds open throughout the operation
  3. Detecting and removing symlinks before directory creation
  4. Anchoring all file operations to directory file descriptors

This eliminates the race window by ensuring all critical operations use directory file descriptors that cannot be replaced by symlinks.

Testing

Fixes #10013

@github-actions
Copy link

github-actions bot commented Jan 9, 2026

GNU testsuite comparison:

GNU test failed: tests/install/basic-1. tests/install/basic-1 is passing on 'main'. Maybe you have to rebase?

…ils#10013)

This commit fixes a TOCTOU (Time-of-Check-Time-of-Use) race condition
in the install -D command where an attacker could replace a directory
component with a symlink between directory creation and file installation,
redirecting writes to an arbitrary location.

Changes:
- Added mkdir_at() and open_file_at() methods to DirFd for safe operations
- Added open_no_follow() to prevent following symlinks when opening directories
- Added create_dir_all_safe() function that uses directory file descriptors
- Modified install -D to use safe traversal functions instead of pathname-based ops
- Added copy_file_safe() function for safe file copying using directory fds
- Added tests to verify the fix prevents symlink race conditions

The fix works by:
1. Using directory file descriptors (mkdirat/openat) instead of pathnames
2. Keeping directory fds open throughout the operation
3. Detecting and removing symlinks before directory creation
4. Anchoring all file operations to directory file descriptors

This eliminates the race window by ensuring all critical operations use
directory file descriptors that cannot be replaced by symlinks.
…x context setting to Linux platforms

This commit updates the conditional compilation for SELinux context settings in the install module to only apply when the target OS is Linux. This change ensures that SELinux-related functionality is not erroneously included on non-Linux platforms, improving compatibility and preventing potential issues.

Changes:
- Modified `#[cfg(feature = "selinux")]` to `#[cfg(all(feature = "selinux", target_os = "linux"))]` in multiple locations to enforce the OS-specific behavior.
@abendrothj abendrothj force-pushed the fix/install-symlink-race-condition-10013 branch from fc7eade to 0b91865 Compare January 15, 2026 09:10
@github-actions
Copy link

GNU testsuite comparison:

GNU test failed: tests/install/basic-1. tests/install/basic-1 is passing on 'main'. Maybe you have to rebase?

…s a file

When -t is used with -D and the target exists as a file (not a directory),
we should fail immediately without printing verbose directory creation
messages. This matches GNU behavior and fixes the failing GNU test
tests/install/basic-1.
@codspeed-hq
Copy link

codspeed-hq bot commented Jan 15, 2026

CodSpeed Performance Report

Merging this PR will degrade performance by 3.46%

Comparing abendrothj:fix/install-symlink-race-condition-10013 (fe7aec2) with main (2c75e71)

Summary

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 280 untouched benchmarks
⏩ 38 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory sort_ascii_utf8_locale 6.7 MB 6.2 MB +7.93%
Memory du_wide_tree[(5000, 500)] 1.2 MB 1.2 MB -3.46%

Footnotes

  1. 38 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

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.

Symlink race in install -D directory creation

1 participant