Skip to content

fix(encode): create temp files in a private directory - #294

Merged
LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
fix/encode-temp-dir
Sep 22, 2026
Merged

LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
fix/encode-temp-dir

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Audit finding carried by this chantier. Refs #220 (RM-44).

The path is fully derived from PID plus a counter starting at 0, in a shared, world-writable directory. `create_dir_all` succeeds on an already-existing directory (or a symlink to one) and `fs::write` follows symlinks, so a local attacker who pre-creates `/tmp/rustmotion_audio_<pid>_0/audio.raw` as a symlink gets an arbitrary file truncated and overwritten with PCM bytes under the rendering user's identity; PID space is small enough to pre-seed exhaustively. The same class exists in `video_audio.rs:258`, `std::env::temp_dir().join(format!("rustmotion_vidaud_{:016x}.wav", hash))`, where `hash` comes from `DefaultHasher::new()` (SipHash seeded with fixed keys 0,0 — deterministic across processes and machines), so the path is exactly predictable. There, `if wav_path.exists() { return Some(wav_path) }` (line 298) means a pre-planted file is used verbatim as the render's audio, and the `.partial.wav` sibling is handed to `ffmpeg -y` (line 339-340), which follows a symlink at that path.

Refs #220
@LeadcodeDev LeadcodeDev added the bug Something isn't working label Sep 22, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 22, 2026
@LeadcodeDev
LeadcodeDev merged commit 07b8971 into chantier/audit-2026-09 Sep 22, 2026
0 of 3 checks passed
LeadcodeDev added a commit that referenced this pull request Sep 22, 2026
The path is fully derived from PID plus a counter starting at 0, in a shared, world-writable directory. `create_dir_all` succeeds on an already-existing directory (or a symlink to one) and `fs::write` follows symlinks, so a local attacker who pre-creates `/tmp/rustmotion_audio_<pid>_0/audio.raw` as a symlink gets an arbitrary file truncated and overwritten with PCM bytes under the rendering user's identity; PID space is small enough to pre-seed exhaustively. The same class exists in `video_audio.rs:258`, `std::env::temp_dir().join(format!("rustmotion_vidaud_{:016x}.wav", hash))`, where `hash` comes from `DefaultHasher::new()` (SipHash seeded with fixed keys 0,0 — deterministic across processes and machines), so the path is exactly predictable. There, `if wav_path.exists() { return Some(wav_path) }` (line 298) means a pre-planted file is used verbatim as the render's audio, and the `.partial.wav` sibling is handed to `ffmpeg -y` (line 339-340), which follows a symlink at that path.

Refs #220
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant