Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
4.7 KiB
Minecraft / Mojang — CWE-407 Disclosure Brief
2026-03-26 · Confidential pre-disclosure
Finding
One exponential-complexity defect in Minecraft's tag dependency sorter. Unpatched. It causes StackOverflow during world load on large modpacks and produces measured 116× world load speedup when patched. We have benchmark data, a working patch, and a live demo.
The Defect
minecraft-0001 (UNPATCHED — CRITICAL): DependencySorter.java — isCyclic()
The isCyclic() method performs a recursive DFS to detect cycles in the tag dependency graph. It carries no visited set. Every node can be revisited an unbounded number of times.
On a diamond dependency chain of depth D (A → B,C; B → D; C → D), the recursion tree has no pruning:
- Node D is visited once from B, once from C
- Node at depth D-1 visits D twice each
- Node at depth D-2 visits depth D-1 twice each
- Total visits at depth D: 2^D
TagLoader calls this on every world load, for every tag group, for every namespace. This is not a degenerate edge case — it is the normal execution path for any modpack that uses tag inheritance.
Complexity Proof
Diamond chain of depth D: one root, branching factor 2 at each level, converging at each level (diamond, not tree). With no visited set, DFS re-explores both subtrees at every convergence point.
| D | Node visits |
|---|---|
| 10 | 1,024 |
| 20 | 1,048,576 |
| 24 | 16,777,216 |
| 30 | 1,073,741,824 |
Vanilla Minecraft modpacks in the wild reach D=10-15 in large Forge/NeoForge packs. Our enriched benchmark runs D=24 with 300 namespaces.
Enriched benchmark result: vanilla server StackOverflows and fails to start. Patched server starts normally. World load time: 116× faster (patched vs. unpatched, measured at D=24/300NS). 30-player stress test with fireworks: patched server stable throughout; unpatched server hits unplayable lag before the StackOverflow.
For comparison, the two other defects found in the same codebase are minor:
- minecraft-0002:
PistonStructureResolver.java—List<BlockPos>.contains()in piston chain resolution. Bounded at 12 blocks max by game design. Not worth fixing. - create-0001 (Create mod):
TrackGraph.java:findDisconnectedGraphs—ArrayList.remove(0)is O(n) shift in BFS frontier. Replace withArrayDeque. Low severity, unbounded in theory but not hot in practice.
Impact
Every Minecraft server running a modpack with tag inheritance depth > ~15. Affected platforms:
- Forge and NeoForge (primary modding platforms, millions of monthly active users)
- Fabric (second-largest modding platform)
- Any server hosting a tech modpack: Create, Applied Energistics, Thermal Expansion, etc.
The exponential is invisible at low D — vanilla Minecraft is mostly fine. It becomes catastrophic as modpacks grow. Large modpacks (1.19+, 300+ mods) are already at D values where world load takes minutes instead of seconds. At D=24 with namespace multiplier, vanilla StackOverflows entirely.
This is a serverside defect. It fires on every /reload, every server restart, every world load. Server operators and hosting providers running large modpacks are the primary affected population.
The Fix
Add a Set<Identifier> visited to the isCyclic() traversal. Return immediately if the current node has already been visited. This converts the exponential recursion to standard O(V+E) DFS.
// Before — exponential, no visited set
private boolean isCyclic(Identifier node, List<Identifier> path) {
for (Identifier dep : getDependencies(node)) {
if (path.contains(dep) || isCyclic(dep, path))
return true;
}
return false;
}
// After — O(V+E), visited set prunes re-exploration
private boolean isCyclic(Identifier node, List<Identifier> path,
Set<Identifier> visited) {
if (visited.contains(node)) return false;
visited.add(node);
for (Identifier dep : getDependencies(node)) {
if (path.contains(dep) || isCyclic(dep, path, visited))
return true;
}
return false;
}
One additional improvement: replace path.contains(dep) (also linear) with a LinkedHashSet<Identifier> for the path itself — O(1) cycle detection for the ancestor check as well.
What We Ask
- Confirm receipt and assign a tracker reference (bugs.mojang.com or internal).
- Assess severity — this is a StackOverflow / DoS on world load for large modpacks, reproducible and benchmarked.
- We have a working patch and benchmark harness available on request.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
- We will credit Mojang in the public disclosure. Preferred acknowledgment format welcome.
Contact: see cover email. This brief is confidential until coordinated disclosure.