Skip to content

renderSVG: limits against denial of service - #81

Closed
matmen wants to merge 1 commit into
masterfrom
svg-limits
Closed

matmen wants to merge 1 commit into
masterfrom
svg-limits

Conversation

@matmen

@matmen matmen commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Image.renderSVG could be made to use unbounded time or memory with small crafted SVGs. This PR adds three limits against that.

input before after
"Billion laughs": nine levels with ten <use> each (1 kB, 10⁹ rectangles) killed (memory) Error after 0.57 s
stroke-dasharray="0.0001" on a long line killed (memory) stroked solid, 12 ms
100,000 nested <g> Maximum call stack size exceeded renders, 144 ms

Changes (src/render/svg.ts)

  • <use> limit: elements rendered through <use> references are counted, and the render throws after 100,000. librsvg has a similar limit. Only instanced elements count, because only they can multiply; direct elements are bounded by the file size, so large plain SVGs are unaffected.
  • Dashes: a dash pattern that would produce more than 1,000,000 dashes strokes the path solid, as Skia does.
  • Deep nesting: the pass that collects IDs and style sheets uses an explicit stack instead of recursion. Document order is preserved, so the first ID still wins.

Reference cycles (use → use, gradients, clip paths, masks) were already caught. The tests confirm that.

Testing

  • Fuzzing: 540 damaged variants of the test SVGs (truncated, or with junk such as broken references, huge numbers and tiny dash arrays inserted) produce no hangs and no slow cases.
  • Existing renders: all test targets are unchanged.
  • tests/regressions.js: covers billion laughs, the fine dash pattern and deep nesting.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJgRK5qpwJEbHPuFv6kgAQ

- elements rendered through <use> references are counted, more than
  100,000 throw (like librsvg's limit): nine levels of ten uses each made
  a billion rectangles from a 1 kB file and killed the process
- dash patterns of more than a million dashes stroke the path solid, as
  Skia does (stroke-dasharray="0.0001" on a long line took any memory)
- the document walk that collects ids and style sheets uses a stack, so
  100,000 nested elements no longer overflow it

Found with crafted SVGs and 540 damaged variants of the test SVGs (no
hangs or slow cases left); test renders are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJgRK5qpwJEbHPuFv6kgAQ
@matmen matmen closed this Oct 16, 2026
@matmen
matmen deleted the svg-limits branch October 16, 2026 22:37
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