Skip to content

Fix overflow panic when filling paths with extreme coordinates - #178

Closed
StefanoD wants to merge 1 commit into
linebender:mainfrom
StefanoD:fix-933-fill-coord-overflow
Closed

Fix overflow panic when filling paths with extreme coordinates#178
StefanoD wants to merge 1 commit into
linebender:mainfrom
StefanoD:fix-933-fill-coord-overflow

Conversation

@StefanoD

@StefanoD StefanoD commented Jun 14, 2026

Copy link
Copy Markdown

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

Generated by Claude

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>
@StefanoD StefanoD closed this by deleting the head repository Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant