Where: src/shared/file.c:339-357 (inplace branch: openat O_WRONLY|O_CREAT|O_NOFOLLOW, no O_TRUNC, no mode reset when metadata absent).
Problem: In-place path opens an existing destination file without truncation or re-creating it. With use_metadata=false no fchmod is done, so original mode/ownership persist; if root-owned setuid, root writes do not clear setuid -> attacker-controlled content into root-owned setuid file (local privesc). Shorter payload also leaves stale trailing bytes.
Fix: In the inplace branch always normalize permissions to metadata safe_mode (or 0644) stripping setuid/setgid/sticky, and truncate (O_TRUNC or ftruncate) before/at write so stale trailing bytes never survive.
Add unit test.
Found by security audit of `dev` (9f8b588).
**Where:** src/shared/file.c:339-357 (inplace branch: openat O_WRONLY|O_CREAT|O_NOFOLLOW, no O_TRUNC, no mode reset when metadata absent).
**Problem:** In-place path opens an existing destination file without truncation or re-creating it. With use_metadata=false no fchmod is done, so original mode/ownership persist; if root-owned setuid, root writes do not clear setuid -> attacker-controlled content into root-owned setuid file (local privesc). Shorter payload also leaves stale trailing bytes.
**Fix:** In the inplace branch always normalize permissions to metadata safe_mode (or 0644) stripping setuid/setgid/sticky, and truncate (O_TRUNC or ftruncate) before/at write so stale trailing bytes never survive.
Add unit test.
Fixed on dev via fix/security-hardening (2234879): --inplace overwrites now ftruncate to payload length and normalize mode (metadata safe_mode or 0644, special bits stripped) after write. Unit tests added.
CI run #461: all jobs green (lint, build-and-test, sanitizers address+undefined, fuzz-build, coverage, valgrind). Unit 25/25, integration 83 passed / 10 skipped / 1 xpassed. Closing.
**Resolved on `dev`** (HEAD f7c6c91).
Fixed on dev via fix/security-hardening (2234879): --inplace overwrites now ftruncate to payload length and normalize mode (metadata safe_mode or 0644, special bits stripped) after write. Unit tests added.
CI run #461: all jobs green (lint, build-and-test, sanitizers address+undefined, fuzz-build, coverage, valgrind). Unit 25/25, integration 83 passed / 10 skipped / 1 xpassed. Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found by security audit of
dev(9f8b588).Where: src/shared/file.c:339-357 (inplace branch: openat O_WRONLY|O_CREAT|O_NOFOLLOW, no O_TRUNC, no mode reset when metadata absent).
Problem: In-place path opens an existing destination file without truncation or re-creating it. With use_metadata=false no fchmod is done, so original mode/ownership persist; if root-owned setuid, root writes do not clear setuid -> attacker-controlled content into root-owned setuid file (local privesc). Shorter payload also leaves stale trailing bytes.
Fix: In the inplace branch always normalize permissions to metadata safe_mode (or 0644) stripping setuid/setgid/sticky, and truncate (O_TRUNC or ftruncate) before/at write so stale trailing bytes never survive.
Add unit test.
Resolved on
dev(HEADf7c6c91).Fixed on dev via fix/security-hardening (
2234879): --inplace overwrites now ftruncate to payload length and normalize mode (metadata safe_mode or 0644, special bits stripped) after write. Unit tests added.CI run #461: all jobs green (lint, build-and-test, sanitizers address+undefined, fuzz-build, coverage, valgrind). Unit 25/25, integration 83 passed / 10 skipped / 1 xpassed. Closing.