Skip to content

Commit 428026d

Browse files
dfa1claude
andcommitted
fix(reader): cap array-node recursion depth
convertArrayNode recursed through child nodes with no depth guard. A crafted or self-referential FlatBuffer array tree drove unbounded recursion into StackOverflowError — an Error, bypassing the VortexException contract. Add MAX_ARRAY_TREE_DEPTH (64) and thread depth, mirroring the layout/DType guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 93f8d5f commit 428026d

1 file changed

Lines changed: 15 additions & 3 deletions

File tree

reader/src/main/java/io/github/dfa1/vortex/reader/FlatSegmentDecoder.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
/// FlatBuffer parsing, buffer-offset arithmetic, and encoding-spec lookup.
2727
public final class FlatSegmentDecoder {
2828

29+
/// Hard cap on array-node recursion depth. The encoded array tree nests through child nodes
30+
/// (validity, patches, run-ends, dictionary codes/values, …); a crafted or self-referential
31+
/// FlatBuffer can drive [#convertArrayNode] into unbounded recursion and a [StackOverflowError]
32+
/// — an `Error`, so it would bypass the [io.github.dfa1.vortex.core.error.VortexException]
33+
/// contract. 64 is well past any real encoding's nesting.
34+
static final int MAX_ARRAY_TREE_DEPTH = 64;
35+
2936
private final ReadRegistry registry;
3037

3138
/// Creates a decoder backed by the given registry.
@@ -64,20 +71,25 @@ public Array decode(MemorySegment seg, List<String> encodingSpecs,
6471
dataOffset += bufDesc.length();
6572
}
6673

67-
ArrayNode rootNode = convertArrayNode(fbArray.root(), encodingSpecs);
74+
ArrayNode rootNode = convertArrayNode(fbArray.root(), encodingSpecs, 0);
6875
var ctx = new DecodeContext(rootNode, dtype, rowCount, bufs, registry, arena);
6976
return registry.decode(ctx);
7077
}
7178

7279
private static ArrayNode convertArrayNode(
7380
io.github.dfa1.vortex.core.fbs.FbsArrayNode fbs,
74-
List<String> encodingSpecs
81+
List<String> encodingSpecs,
82+
int depth
7583
) {
84+
if (depth > MAX_ARRAY_TREE_DEPTH) {
85+
throw new io.github.dfa1.vortex.core.error.VortexException(
86+
"array tree depth exceeds limit (" + MAX_ARRAY_TREE_DEPTH + ")");
87+
}
7688
String rawEncodingId = encodingSpecs.get(fbs.encoding());
7789

7890
ArrayNode[] children = new ArrayNode[fbs.childrenLength()];
7991
for (int i = 0; i < children.length; i++) {
80-
children[i] = convertArrayNode(fbs.children(i), encodingSpecs);
92+
children[i] = convertArrayNode(fbs.children(i), encodingSpecs, depth + 1);
8193
}
8294

8395
int[] bufferIndices = new int[fbs.buffersLength()];

0 commit comments

Comments
 (0)