Repository navigation
perf(preload): bound the extracted video frame cache - #240
Closed
LeadcodeDev wants to merge 1 commit into
Closed
LeadcodeDev wants to merge 1 commit into
LeadcodeDev wants to merge 1 commit into
Conversation
53 tasks
LeadcodeDev
force-pushed
the
perf/gif-decode-stampede
branch
from
September 22, 2026 06:10
9ab7bc9 to
4d15a91
Compare
LeadcodeDev
force-pushed
the
perf/video-frame-cache-budget
branch
from
September 22, 2026 06:10
2eeb8b8 to
e10ffb1
Compare
LeadcodeDev
force-pushed
the
perf/gif-decode-stampede
branch
from
September 22, 2026 08:34
4d15a91 to
c89ddad
Compare
LeadcodeDev
force-pushed
the
perf/video-frame-cache-budget
branch
from
September 22, 2026 08:34
e10ffb1 to
f6a17df
Compare
LeadcodeDev
force-pushed
the
perf/gif-decode-stampede
branch
from
September 22, 2026 08:44
c89ddad to
1c12b20
Compare
Triggering scenario: a scenario JSON declaring a video component with style.width/height of 1920x1080 inside a 60 s scene at 30 fps. .output() (line 234) collects ffmpeg's entire rawvideo stdout into one Vec<u8>: 1800 frames x 8.3 MB = 15 GB. data then stays borrowed for the whole copy loop, so the per-frame .to_vec() copies build a second 15 GB structure alongside it — 30 GB peak before output.stdout is dropped. The result is inserted into the process-global VIDEO_FRAME_CACHE (assets.rs:168), which has no eviction and no clear function at all. There is no size, duration, or resolution cap anywhere on this path, and the inputs are entirely scenario-controlled. Under --watch this compounds: changing the component's style.width mints a new "src:WxH" key while the old multi-GB entry is never freed. Secondary hazard on the same line: width * height * 4 is plain u32 arithmetic, so a declared 65536x16384 wraps to 0 and data.len() / frame_size panics with a divide-by-zero. Fix: Stream ffmpeg's stdout frame-by-frame into the frames vector instead of .output() (spawn + read exact frame_size chunks from the pipe), which removes the 2x peak. Add a byte budget for the cache and refuse (with a clear error) sources that exceed it, and make frame_size a checked u64 computation. Refs #220
LeadcodeDev
force-pushed
the
perf/video-frame-cache-budget
branch
from
September 22, 2026 08:44
f6a17df to
5e0f4c1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Severity Medium, category quality. Location:
crates/rustmotion/src/engine/preload.rs:238Impact
Triggering scenario: a scenario JSON declaring a
videocomponent withstyle.width/heightof 1920x1080 inside a 60 s scene at 30 fps..output()(line 234) collects ffmpeg's entire rawvideo stdout into oneVec<u8>: 1800 frames x 8.3 MB = 15 GB.datathen stays borrowed for the whole copy loop, so the per-frame.to_vec()copies build a second 15 GB structure alongside it — 30 GB peak beforeoutput.stdoutis dropped. The result is inserted into the process-globalVIDEO_FRAME_CACHE(assets.rs:168), which has no eviction and noclearfunction at all. There is no size, duration, or resolution cap anywhere on this path, and the inputs are entirely scenario-controlled. Under--watchthis compounds: changing the component'sstyle.widthmints a new"src:WxH"key while the old multi-GB entry is never freed. Secondary hazard on the same line:width * height * 4is plain u32 arithmetic, so a declared 65536x16384 wraps to 0 anddata.len() / frame_sizepanics with a divide-by-zero.Fix
Stream ffmpeg's stdout frame-by-frame into the
framesvector instead of.output()(spawn + read exactframe_sizechunks from the pipe), which removes the 2x peak. Add a byte budget for the cache and refuse (with a clear error) sources that exceed it, and makeframe_sizea checkedu64computation.Evidence the audit read
Part of the September 2026 audit remediation chantier. Refs #220 (RM-21).