Conversation
When anti-aliased filling a path whose device-space vertical bounds lie far outside the clip, `start_y`/`stop_y` (the path bounds) were shifted up for supersampling with a plain `<<`. A large-negative bound then overflowed `i32` and wrapped into a large-positive value, escaping the subsequent clip clamp and breaking `walk_edges`' `last_y >= curr_y` invariant. Use a saturating shift so out-of-range bounds stay on the correct side and get clamped to the clip as intended. In-range bounds are unaffected. Originally reported against resvg: linebender/resvg#933 Reproduced with: <svg height="32" viewBox="0 0 8.4.9" width="32" x="w"> <g m=""><g><path d="m3 6h8v-158758583"/></g></g> </svg> Crash dump (debug build): thread 'main' panicked at tiny-skia/src/scan/path.rs:221:13: assertion failed: edges[curr_idx].last_y >= curr_y as i32 3: tiny_skia::scan::path::walk_edges 4: tiny_skia::scan::path::fill_path_impl 5: tiny_skia::scan::path_aa::fill_path_impl 6: tiny_skia::scan::path_aa::fill_path 7: tiny_skia::painter::<impl tiny_skia::pixmap::PixmapMut>::fill_path Crash dump (release build): thread 'main' panicked at tiny-skia/src/pipeline/mod.rs:181:31: range start index 15006310411 out of range for slice of length 1024 3: tiny_skia::pipeline::lowp::load_dst_tail 4: tiny_skia::pipeline::lowp::start 5: <tiny_skia::pipeline::blitter::RasterPipelineBlitter as tiny_skia::blitter::Blitter>::blit_anti_h 6: tiny_skia::scan::path_aa::SuperBlitter::flush 7: tiny_skia::scan::path_aa::fill_path_impl Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
malbarbo
added a commit
to malbarbo/sinteract
that referenced
this pull request
Sep 29, 2026
tiny-skia 0.12 overflows an i32 when it supersamples a path whose bounds reach 2^29 output pixels above the canvas, and 2^31 left of it. The render then panics, and in a release build it can abort, so one frame from the wire with a far coordinate, a wide stroke or a large text scale kills the frontend. The fix upstream, linebender/tiny-skia#180, is not released. A fill, a stroke, a clip and a text now draw nothing when their bounds in output pixels reach past 2^28. A stroke first checks the path grown by miter_limit widths, and outlines itself only when that bound fails, so a common stroke costs no extra work. A clip that reaches too far hides what it holds, as one with no area does. Element and the schema say a renderer may draw nothing for such an element.
This branch has not been deployed
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.
When anti-aliased filling a path whose device-space vertical bounds lie far outside the clip,
start_y/stop_y(the path bounds) were shifted up for supersampling with a plain<<. A large-negative bound then overflowedi32and wrapped into a large-positive value, escaping the subsequent clip clamp and breakingwalk_edges'last_y >= curr_yinvariant.Use a saturating shift so out-of-range bounds stay on the correct side and get clamped to the clip as intended. In-range bounds are unaffected.
Originally reported against resvg: linebender/resvg#933
Reproduced with:
Crash dump (debug build):
Crash dump (release build):
Generated by Claude
Note: I accidentally closed the original PR (#178) by deleting my fork, which auto-closed it. This reopens the same change — the branch and commits are unchanged.