Skip to content

uucore: add safe_copy module with TOCTOU-resistant copy primitives#12130

Merged
sylvestre merged 2 commits into
uutils:mainfrom
sylvestre:feat/safe-copy-foundation
May 3, 2026
Merged

uucore: add safe_copy module with TOCTOU-resistant copy primitives#12130
sylvestre merged 2 commits into
uutils:mainfrom
sylvestre:feat/safe-copy-foundation

Conversation

@sylvestre
Copy link
Copy Markdown
Contributor

to have a better way to fix the TOCTOU issues

sylvestre added 2 commits May 2, 2026 23:54
Three patches in flight (cp uutils#10011, cp uutils#10017, mv uutils#10014) each
independently reinvent the same security primitive: open the source
with optional O_NOFOLLOW, create the destination with a restrictive
0o600 initial mode, copy bytes, and let the caller fix permissions
later. The cp variants ship duplicated across platform/{linux,
other_unix}.rs; mv hand-rolls a similar block in rename_file_fallback
and notably forgets the O_NOFOLLOW guard.

Add a single home for the primitive:

    pub fn open_source(path, nofollow) -> io::Result<File>
    pub fn create_dest_restrictive(path, nofollow) -> io::Result<File>
    pub fn safe_copy_file(src, dst, nofollow) -> io::Result<u64>

The destination open also takes nofollow: without it, an attacker who
swaps the destination path to a symlink between the caller's check and
the open redirects the O_TRUNC/O_CREAT to whatever file the symlink
points at. Threading the same flag through safe_copy_file gives
symmetric protection on both ends.

Tests cover: open_source follows or ELOOPs based on the flag and
accepts regular files either way; create_dest_restrictive sets 0o600
on new inodes, preserves existing inode modes, and rejects symlink
swaps when nofollow is set (verifying the would-be victim is
untouched); safe_copy_file end-to-end plus the nofollow-rejects-
symlink cases for both source and destination. Module is gated
behind a new uucore feature `safe-copy`, matching how `safe-traversal`
and `fsxattr` are organized.

Subsequent patches will rewire cp's platform helpers and mv's
cross-device fallback to use these primitives, removing the duplication
and giving mv O_NOFOLLOW for free.
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 3, 2026

Merging this PR will improve performance by 3.11%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 310 untouched benchmarks
⏩ 46 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation du_max_depth_balanced_tree[(6, 4, 10)] 25.9 ms 25.2 ms +3.11%

Comparing sylvestre:feat/safe-copy-foundation (b1096ce) with main (3057b7d)

Open in CodSpeed

Footnotes

  1. 46 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.

@sylvestre sylvestre marked this pull request as ready for review May 3, 2026 09:35
@sylvestre sylvestre merged commit fdafdca into uutils:main May 3, 2026
166 of 167 checks passed
@sylvestre sylvestre deleted the feat/safe-copy-foundation branch May 3, 2026 09:35
sylvestre added a commit to sylvestre/coreutils that referenced this pull request May 3, 2026
nonontb pushed a commit to nonontb/coreutils that referenced this pull request May 28, 2026
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.

1 participant