Description
The vector_graphics_compiler tool is vulnerable to Stack Overflow crashes and CPU/Memory Denial of Service (DoS) exhaustion during the AST parsing and resolution phases if an SVG contains circular references or exponential nested expansions.
We have identified two distinct vulnerability vectors:
- Infinite Recursion (Stack Overflow): Circular reference loops inside masks, patterns, deferred
<use> nodes, or <clipPath> definitions lead to infinite recursion, causing the Dart thread to run out of stack space and crash the compiler.
- Exponential DAG Expansion (Billion Laughs / CPU DoS): Acyclic Directed Acyclic Graphs (DAGs) that reference lower-level groups multiple times do not trigger cycle detection, but expand exponentially. A 30-level deep DAG expands to over 1 billion nodes, locking up the CPU and exhausting heap memory (OOM), crashing downstream compile servers or client builders.
Steps to Reproduce
The bugs can be triggered by compiling any SVG that contains circular or highly nested references. Below are three minimal reproduction SVGs:
1. Circular <use> Node Loop (Triggers Stack Overflow)
<svg viewBox="0 0 100 100">
<g id="groupA">
<use xlink:href="#groupB" />
</g>
<g id="groupB">
<use xlink:href="#groupA" />
</g>
<use xlink:href="#groupA" />
</svg>
2. Circular ClipPath Node Loop (Triggers Stack Overflow)
<svg viewBox="0 0 100 100">
<g id="groupA">
<use xlink:href="#groupB" />
</g>
<g id="groupB">
<use xlink:href="#groupA" />
</g>
<clipPath id="clip1">
<use xlink:href="#groupA" />
</clipPath>
<rect width="100" height="100" fill="blue" clip-path="url(#clip1)"/>
</svg>
3. Exponential DAG Expansion (Triggers CPU/Memory DoS)
<svg viewBox="0 0 100 100">
<defs>
<path id="leaf" d="M 0,0 L 10,10" />
<g id="lvl1"><use href="#leaf" /><use href="#leaf" /></g>
<g id="lvl2"><use href="#lvl1" /><use href="#lvl1" /></g>
<!-- ... nested up to lvl30 ... -->
<g id="lvl30"><use href="#lvl29" /><use href="#lvl29" /></g>
</defs>
<use href="#lvl30" />
</svg>
Stack Trace (Circular Reference Crash)
When running the compiler parser test with the circular clip-path SVG, it crashes with a Stack Overflow:
Stack Overflow
package:vector_graphics_compiler/src/svg/parser.dart 1705:5 _Resolver.getClipPath.extractPathsFromNode
package:vector_graphics_compiler/src/svg/parser.dart 1721:16 _Resolver.getClipPath.extractPathsFromNode
package:vector_graphics_compiler/src/svg/parser.dart 1721:16 _Resolver.getClipPath.extractPathsFromNode
package:vector_graphics_compiler/src/svg/parser.dart 1719:9 _Resolver.getClipPath.extractPathsFromNode
package:vector_graphics_compiler/src/svg/parser.dart 1721:16 _Resolver.getClipPath.extractPathsFromNode
...
package:vector_graphics_compiler/src/svg/resolver.dart 30:48 ResolvingVisitor.visitClipNode
package:vector_graphics_compiler/src/svg/node.dart 348:20 ClipNode.accept
package:vector_graphics_compiler/src/svg/resolver.dart 187:16 ResolvingVisitor.visitViewportNode
package:vector_graphics_compiler/src/svg/node.dart 133:20 ViewportNode.accept
package:vector_graphics_compiler/src/svg/parser.dart 864:27 SvgParser.parse
Expected Behavior
The compiler should be robust against all forms of reference-based resource exhaustion. Instead of crashing or hanging, it should:
- Break Circular Loops: Perform DFS active ancestor tracking to identify circular loops in masks, patterns, deferred nodes, and clip paths, breaking them gracefully by returning empty fallback representations (allowing the base elements to render safely without crashing).
- Limit Cumulative Expansions: Enforce a strict, cumulative reference expansion safety ceiling (e.g., capping total expansions at 10,000) to cleanly abort compilation on malicious exponential DAG expansion payloads (Billion Laughs).
Proposed Proposal / Mitigation
Add two layers of defense inside vector_graphics_compiler (resolver.dart and parser.dart):
- DFS Cycle-Guards: Wrap recursive AST resolution in
try/finally blocks, using Set<String> scopes (_activeMasks, _activePatterns, _activeDeferred, and activeDeferred) to track and break circular dependencies.
- Global Cumulative Expansion Cap: Implement a monotonic counter (
_deferredExpansionCount) that accumulates the number of resolved reference events. If this counter crosses 10,000, throw a handled StateError, preventing compile-time thread hangs and Out-Of-Memory crashes.
Description
The
vector_graphics_compilertool is vulnerable to Stack Overflow crashes and CPU/Memory Denial of Service (DoS) exhaustion during the AST parsing and resolution phases if an SVG contains circular references or exponential nested expansions.We have identified two distinct vulnerability vectors:
<use>nodes, or<clipPath>definitions lead to infinite recursion, causing the Dart thread to run out of stack space and crash the compiler.Steps to Reproduce
The bugs can be triggered by compiling any SVG that contains circular or highly nested references. Below are three minimal reproduction SVGs:
1. Circular
<use>Node Loop (Triggers Stack Overflow)2. Circular ClipPath Node Loop (Triggers Stack Overflow)
3. Exponential DAG Expansion (Triggers CPU/Memory DoS)
Stack Trace (Circular Reference Crash)
When running the compiler parser test with the circular clip-path SVG, it crashes with a Stack Overflow:
Expected Behavior
The compiler should be robust against all forms of reference-based resource exhaustion. Instead of crashing or hanging, it should:
Proposed Proposal / Mitigation
Add two layers of defense inside
vector_graphics_compiler(resolver.dartandparser.dart):try/finallyblocks, usingSet<String>scopes (_activeMasks,_activePatterns,_activeDeferred, andactiveDeferred) to track and break circular dependencies._deferredExpansionCount) that accumulates the number of resolved reference events. If this counter crosses10,000, throw a handledStateError, preventing compile-time thread hangs and Out-Of-Memory crashes.