Skip to content

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

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

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

Conversation

@StefanoD

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


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.

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>
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