cp: apply the finalize chmod without following a symlink - #13630
cp: apply the finalize chmod without following a symlink#13630sylvestre wants to merge 2 commits into
Conversation
|
GNU testsuite comparison: |
Merging this PR will regress 5 benchmarks
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | cp_preserve_metadata[(5, 4, 10)] |
161.8 ms | 170.5 ms | -5.1% |
| ❌ | Simulation | cp_recursive_balanced_tree[(5, 4, 10)] |
147.4 ms | 155.2 ms | -5.03% |
| ❌ | Simulation | cp_archive_balanced_tree[(5, 4, 10)] |
173.8 ms | 182.8 ms | -4.92% |
| ❌ | Simulation | cp_recursive_wide_tree[(6000, 800)] |
374.6 ms | 393.4 ms | -4.76% |
| ❌ | Simulation | cp_recursive_deep_tree[(120, 4)] |
24.9 ms | 25.6 ms | -3.05% |
| ⚡ | Simulation | df_with_path |
698.8 µs | 570.8 µs | +22.43% |
| ⚡ | Simulation | cksum_crc32b |
41.9 ms | 40.2 ms | +4.31% |
| ⚡ | Simulation | numfmt_to_si_precision[10000] |
94.9 ms | 92 ms | +3.2% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing sylvestre:cp-finalize-chmod-nofollow (2c4eb31) with main (0e791b1)2
Footnotes
-
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. ↩
-
No successful run was found on
main(7c87ab0) during the generation of this report, so 0e791b1 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
Both chmod sites in copy_file/copy_attributes decide whether to act by testing dest for being a symlink, but the test happens long before the chmod: in copy_file the lstat is taken before the copy starts, so the gap spans the whole file copy. A symlink swapped into dest during that gap redirected the mode change onto the link's target, off the destination tree. Measured with a tight unlink/symlink loop against a 20MB copy, 40 rounds each, an unrelated 0600 file taken to 0777: before 12/40 and 13/40 rounds hit after 0/40 GNU 9.10 2/40 and 3/40 Anchor the chmod to the parent directory fd with NoFollow, reusing DirFd::chmod_at. EOPNOTSUPP/ELOOP mean dest is now a symlink, which is exactly the "do nothing" case both guards already describe, so it maps to success. Note GNU is affected by the same class at a lower rate, so this is hardening beyond GNU rather than a compatibility fix. The remaining path-based steps in copy_attributes (timestamps, xattr, ACL, SELinux) are unchanged.
aa8d8dc to
99e6268
Compare
|
@codspeedbot fix this regression |
Fixed and pushed to this branch. Cause. The new no-follow chmod opened the destination's parent directory to obtain a dirfd, then chmod'ed the final component through it. That is one extra
Fix. The chmod is now issued against Verification. Reproduced the report locally in CPU simulation mode against
The One drive-by: the new function had been inserted between Summary
Was this response helpful? React with 👍 or 👎 to help us improve CodSpeedBot. |
|
Hello @sylvestre, sorry about the failed request. I fixed the bug that prevented the agent from replying here and restarted the latest request. It opened a PR at #13683 |
copy_fileandcopy_attributesboth decide whether to chmod the destination by testing it for being a symlink, but the test happens long before the chmod. Incopy_filethelstatis taken at function entry, before the copy starts, so the gap spans the entire file copy - proportional to file size, not a few instructions. A symlink swapped intodestduring that gap redirects the mode change onto the link's target, outside the destination tree.