fix(gif): cap decoded gif dimensions and frame count - #241
Merged
Merged
Conversation
53 tasks
LeadcodeDev
force-pushed
the
perf/video-frame-cache-budget
branch
from
September 22, 2026 06:10
2eeb8b8 to
e10ffb1
Compare
LeadcodeDev
force-pushed
the
fix/gif-decode-bomb
branch
from
September 22, 2026 06:10
c6adb3c to
7fa956d
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
fix/gif-decode-bomb
branch
from
September 22, 2026 08:34
7fa956d to
cb20470
Compare
LeadcodeDev
force-pushed
the
perf/video-frame-cache-budget
branch
from
September 22, 2026 08:44
f6a17df to
5e0f4c1
Compare
LeadcodeDev
force-pushed
the
fix/gif-decode-bomb
branch
from
September 22, 2026 08:44
cb20470 to
4f6243e
Compare
LeadcodeDev
changed the base branch from
perf/video-frame-cache-budget
to
chantier/audit-2026-09
September 22, 2026 08:53
LeadcodeDev
force-pushed
the
fix/gif-decode-bomb
branch
from
September 22, 2026 09:00
4f6243e to
40ec466
Compare
`decoder.width()/height()` come straight from the GIF logical-screen descriptor (2 bytes each, max 65535). A ~200-byte crafted GIF declaring 65535×65535 makes line 129 request a single 17.2 GB allocation before a single pixel is decoded. Worse, the `while` loop then pushes a *full-canvas clone* per animation frame (line 141) with no frame-count limit — a 4000×4000 canvas with 500 frames is 32 TB of `Vec<u8>` — and the result is inserted into the global `gif_cache()` (`renderer/assets.rs:32`) which is never evicted. A scenario whose `gif` src points at such a file aborts the render process (Rust allocation failure = abort, not a catchable error), which is a denial of service on any batch/CI renderer fed third-party scenarios or media. Refs #220
LeadcodeDev
force-pushed
the
fix/gif-decode-bomb
branch
from
September 22, 2026 09:05
40ec466 to
e4bfd75
Compare
LeadcodeDev
added a commit
that referenced
this pull request
Sep 22, 2026
`decoder.width()/height()` come straight from the GIF logical-screen descriptor (2 bytes each, max 65535). A ~200-byte crafted GIF declaring 65535×65535 makes line 129 request a single 17.2 GB allocation before a single pixel is decoded. Worse, the `while` loop then pushes a *full-canvas clone* per animation frame (line 141) with no frame-count limit — a 4000×4000 canvas with 500 frames is 32 TB of `Vec<u8>` — and the result is inserted into the global `gif_cache()` (`renderer/assets.rs:32`) which is never evicted. A scenario whose `gif` src points at such a file aborts the render process (Rust allocation failure = abort, not a catchable error), which is a denial of service on any batch/CI renderer fed third-party scenarios or media. Refs #220
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 security. Location:
crates/rustmotion-components/src/gif.rs:129Impact
decoder.width()/height()come straight from the GIF logical-screen descriptor (2 bytes each, max 65535). A ~200-byte crafted GIF declaring 65535×65535 makes line 129 request a single 17.2 GB allocation before a single pixel is decoded. Worse, thewhileloop then pushes a full-canvas clone per animation frame (line 141) with no frame-count limit — a 4000×4000 canvas with 500 frames is 32 TB ofVec<u8>— and the result is inserted into the globalgif_cache()(renderer/assets.rs:32) which is never evicted. A scenario whosegifsrc points at such a file aborts the render process (Rust allocation failure = abort, not a catchable error), which is a denial of service on any batch/CI renderer fed third-party scenarios or media.Fix
Before allocating, reject canvases above a configured budget: check
canvas_w as u64 * canvas_h as u64 * 4against a cap (and compare against the video's own dimensions — nothing larger can be usefully drawn) and returnNonewith a warning, exactly like the existing decode-failure arms. Add a frame-count cap to thewhileloop, and prefer storing only the deltas or re-decoding on demand rather than cloning the whole canvas per frame.Evidence the audit read
Part of the September 2026 audit remediation chantier. Refs #220 (RM-23).