Skip to content

Fix out-of-bounds write when a clip path lies outside the image - #591

Open
Graveflo wants to merge 1 commit into
treeform:masterfrom
Graveflo:clip_bounds
Open

Fix out-of-bounds write when a clip path lies outside the image#591
Graveflo wants to merge 1 commit into
treeform:masterfrom
Graveflo:clip_bounds

Conversation

@Graveflo

@Graveflo Graveflo commented Jul 30, 2026

Copy link
Copy Markdown

FYI

  • discovered and implemented by AI (GPT-5.6-Sol & Opus 5)
  • test fails on master
  • performance assessment at the bottom

Summary

Context.clip with a clip region that lies fully outside the canvas writes
outside the image buffer. Depending on where the write lands it raises
IndexDefect or segfaults inside the SIMD fill.

A clip region outside the canvas is ordinary Canvas2D usage — it is what you get
when clipping to something that has scrolled off — so this is reachable from the
public API without doing anything unusual.

Reproduced against 6.1.0 and against current master.

Reproduction

import pixie

let
  image = newImage(420, 460)
  ctx = image.newContext()

let a = newPath()
a.rect(0, 0, 420, 460)
ctx.clip(a)                  # first clip: mask == nil -> OverwriteBlend

let b = newPath()
b.rect(0, 500, 200, 100)     # entirely below the image
ctx.clip(b)                  # second clip: mask != nil -> MaskBlend

Performance

The patch changes behavior in three of the four off-canvas directions. Only one
of those three has a "before" time worth comparing, because the other two
crashed:

clip region before after
below the canvas out-of-bounds write correct — no meaningful timing to compare
above the canvas out-of-bounds write correct — no meaningful timing to compare
right of the canvas returned immediately, silently dropping the clip correct, and now costs one mask clear
left of the canvas already correct unchanged

So the case that gets slower is the one that previously produced a wrong result
without faulting: a MaskBlend fill whose path lies entirely right of the
canvas, which is the pathWidth == 0 early return.

How much, per ctx.clip() call:

canvas off-canvas clip, before off-canvas clip, after on-canvas clip, for scale
420×460 97 µs 102 µs 104 µs
1000×1000 498 µs 528 µs 531 µs
1920×1080 1,044 µs 1,103 µs 1,104 µs

+5 to +59 µs on a call that already costs 97 to 1,044 µs — about 5%, scaling
with canvas area as any mask operation does.

After the fix, clipping to an off-canvas region costs the same as clipping to an on-canvas one.

@Graveflo
Graveflo marked this pull request as draft July 30, 2026 17:19
@Graveflo
Graveflo marked this pull request as ready for review July 30, 2026 19:08
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