Description
While reviewing the block-loading pipeline in Music Blocks, I noticed that the circular connection validation inside:
only checks for direct self-references (A → A) and does not detect larger cyclic graphs such as:
or:
Current validation logic:
for (const c in blkData[4]) {
if (blkData[4][c] === blkData[0]) {
console.debug("Circular connection in block data: " + blkData);
return;
}
}
This prevents only direct self-loops.
However, multi-block cycles are still accepted and later processed by recursive traversal functions such as:
_calculateDragGroup
adjustDocks
Both functions contain loop counters (dragLoopCounter, _loopCounter), but these counters only limit iteration count after recursion has already expanded the JavaScript call stack.
For sufficiently deep cyclic or chained graphs, recursion can still grow large enough to trigger:
RangeError: Maximum call stack size exceeded
during project loading or block traversal.
Expected Behavior
Project loading should reject all cyclic block graphs before recursive traversal begins.
Malformed or corrupted project files containing circular connections should fail gracefully instead of triggering recursive traversal loops.
Recursive graph traversal should also avoid unbounded recursive stack growth for large block graphs.
Current Behavior
Only direct self-loops are rejected.
More complex cyclic graphs can still pass validation and later cause recursive traversal between connected blocks.
Example:
or:
These cycles can repeatedly recurse through:
_calculateDragGroup
adjustDocks
eventually overflowing the JavaScript call stack for sufficiently large graphs.
Steps to Reproduce
- Open Music Blocks
- Open DevTools Console
- Run:
const maliciousProject = [
[0, "start", 100, 100, [null, 1, null]],
[1, "action", 200, 100, [0, null, null]]
];
activity.blocks.loadNewBlocks(maliciousProject);
- Observe recursive traversal repeatedly revisiting connected blocks
- Larger cyclic graphs can eventually trigger:
RangeError: Maximum call stack size exceeded
Root Cause
The current validation logic only checks whether a block directly references itself.
It does not validate the overall connection graph for indirect cycles.
Later recursive traversal functions assume the graph is acyclic and recursively walk connections without robust cycle protection.
Suggested Fix
Fix 1 — Detect full graph cycles during project load
Instead of checking only direct self-loops, build a connection graph and run proper DFS/BFS cycle detection before blocks are loaded.
Example approach:
- build adjacency map
- track visited nodes
- detect back edges/cycles
- reject cyclic project graphs before traversal begins
Fix 2 — Replace recursive traversal with iterative traversal
Functions such as:
_calculateDragGroup
adjustDocks
could be converted to iterative traversal using:
This prevents deep recursive call chains and avoids stack overflow even for very large block graphs.
Why This Matters
Malformed or corrupted project files can currently create recursive traversal loops during project loading.
This may lead to:
- browser freezes
- stack overflow errors
- incomplete project loads
- unstable editor state
- crashes for very large cyclic block graphs
Checklist
Description
While reviewing the block-loading pipeline in Music Blocks, I noticed that the circular connection validation inside:
only checks for direct self-references (
A → A) and does not detect larger cyclic graphs such as:or:
Current validation logic:
This prevents only direct self-loops.
However, multi-block cycles are still accepted and later processed by recursive traversal functions such as:
_calculateDragGroupadjustDocksBoth functions contain loop counters (
dragLoopCounter,_loopCounter), but these counters only limit iteration count after recursion has already expanded the JavaScript call stack.For sufficiently deep cyclic or chained graphs, recursion can still grow large enough to trigger:
during project loading or block traversal.
Expected Behavior
Project loading should reject all cyclic block graphs before recursive traversal begins.
Malformed or corrupted project files containing circular connections should fail gracefully instead of triggering recursive traversal loops.
Recursive graph traversal should also avoid unbounded recursive stack growth for large block graphs.
Current Behavior
Only direct self-loops are rejected.
More complex cyclic graphs can still pass validation and later cause recursive traversal between connected blocks.
Example:
or:
These cycles can repeatedly recurse through:
_calculateDragGroupadjustDockseventually overflowing the JavaScript call stack for sufficiently large graphs.
Steps to Reproduce
Root Cause
The current validation logic only checks whether a block directly references itself.
It does not validate the overall connection graph for indirect cycles.
Later recursive traversal functions assume the graph is acyclic and recursively walk connections without robust cycle protection.
Suggested Fix
Fix 1 — Detect full graph cycles during project load
Instead of checking only direct self-loops, build a connection graph and run proper DFS/BFS cycle detection before blocks are loaded.
Example approach:
Fix 2 — Replace recursive traversal with iterative traversal
Functions such as:
_calculateDragGroupadjustDockscould be converted to iterative traversal using:
This prevents deep recursive call chains and avoids stack overflow even for very large block graphs.
Why This Matters
Malformed or corrupted project files can currently create recursive traversal loops during project loading.
This may lead to:
Checklist