From 772bd09c6466f6f4c96a07fda887a27dc01c23a4 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 4 Oct 2022 19:57:40 -0700 Subject: [PATCH] Fix #818 --- release-notes/VERSION-2.x | 2 + .../fasterxml/jackson/core/JsonPointer.java | 65 +++++++++++++++---- .../Fuzz51806JsonPointerParse818Test.java | 2 +- .../ParseSignificandWithSwarTest.java | 2 +- 4 files changed, 57 insertions(+), 14 deletions(-) rename src/test/java/com/fasterxml/jackson/{failing => core/fuzz}/Fuzz51806JsonPointerParse818Test.java (97%) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index cc184b7fea..be1a35bcbb 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -64,6 +64,8 @@ JSON library. `byte[]`/`char[]`/String-with-offsets input #812: Add explicit bounds checks for `JsonFactory.createParser()` methods that take `byte[]`/`char[]`-with-offsets input +#818: Calling `JsonPointer.compile(...)` on very deeply nested expression + throws `StackOverflowErrror` 2.13.4 (03-Sep-2022) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonPointer.java b/src/main/java/com/fasterxml/jackson/core/JsonPointer.java index 4f0614bbcf..0f3a8acf0c 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonPointer.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonPointer.java @@ -562,16 +562,23 @@ private final static int _parseIndex(String str) { } return NumberInput.parseInt(str); } - - protected static JsonPointer _parseTail(String input) { - final int end = input.length(); + + protected static JsonPointer _parseTail(String fullPath) + { + PointerParent parent = null; // first char is the contextual slash, skip - for (int i = 1; i < end; ) { - char c = input.charAt(i); + int i = 1; + int end = fullPath.length(); + + while (i < end) { + char c = fullPath.charAt(i); if (c == '/') { // common case, got a segment - return new JsonPointer(input, input.substring(1, i), - _parseTail(input.substring(i))); + parent = new PointerParent(parent, fullPath, fullPath.substring(1, i)); + fullPath = fullPath.substring(i); + i = 1; + end = fullPath.length(); + continue; } ++i; // quoting is different; offline this case @@ -579,20 +586,32 @@ protected static JsonPointer _parseTail(String input) { // 04-Oct-2022, tatu: Let's decode escaped segment // instead of recursive call StringBuilder sb = new StringBuilder(32); - i = _extractEscapedSegment(input, i, sb); + i = _extractEscapedSegment(fullPath, i, sb); final String segment = sb.toString(); if (i < 0) { // end! - return new JsonPointer(input, segment, EMPTY); + return _buildPath(fullPath, segment, parent); } - return new JsonPointer(input, segment, - _parseTail(input.substring(i))); + parent = new PointerParent(parent, fullPath, segment); + fullPath = fullPath.substring(i); + i = 1; + end = fullPath.length(); + continue; } // otherwise, loop on } // end of the road, no escapes - return new JsonPointer(input, input.substring(1), EMPTY); + return _buildPath(fullPath, fullPath.substring(1), parent); } + private static JsonPointer _buildPath(String fullPath, String segment, + PointerParent parent) { + JsonPointer curr = new JsonPointer(fullPath, segment, EMPTY); + for (; parent != null; parent = parent.parent) { + curr = new JsonPointer(parent.fullPath, parent.segment, curr); + } + return curr; + } + /** * Method called to extract the next segment of the path, in case * where we seem to have encountered a (tilde-)escaped character @@ -665,6 +684,28 @@ protected JsonPointer _constructHead(int suffixLength, JsonPointer last) _matchingElementIndex, next._constructHead(suffixLength, last)); } + /* + /********************************************************** + /* Helper class used to replace call stack (2.14+) + /********************************************************** + */ + + /** + * Helper class used to replace call stack when parsing JsonPointer + * expressions. + */ + private static class PointerParent { + public final PointerParent parent; + public final String fullPath; + public final String segment; + + PointerParent(PointerParent pp, String fp, String sgm) { + parent = pp; + fullPath = fp; + segment = sgm; + } + } + /* /********************************************************** /* Support for JDK serialization (2.14+) diff --git a/src/test/java/com/fasterxml/jackson/failing/Fuzz51806JsonPointerParse818Test.java b/src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz51806JsonPointerParse818Test.java similarity index 97% rename from src/test/java/com/fasterxml/jackson/failing/Fuzz51806JsonPointerParse818Test.java rename to src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz51806JsonPointerParse818Test.java index 48c0529cd4..3dda4cc7ea 100644 --- a/src/test/java/com/fasterxml/jackson/failing/Fuzz51806JsonPointerParse818Test.java +++ b/src/test/java/com/fasterxml/jackson/core/fuzz/Fuzz51806JsonPointerParse818Test.java @@ -1,4 +1,4 @@ -package com.fasterxml.jackson.failing; +package com.fasterxml.jackson.core.fuzz; import com.fasterxml.jackson.core.BaseTest; import com.fasterxml.jackson.core.JsonPointer; diff --git a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/ParseSignificandWithSwarTest.java b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/ParseSignificandWithSwarTest.java index 829cbdea01..867a5ea296 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/ParseSignificandWithSwarTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/ParseSignificandWithSwarTest.java @@ -44,7 +44,7 @@ public void doIllegalTest(String s) { public void doLegalTest(String s) { double actual = significandToDouble(s.getBytes(StandardCharsets.ISO_8859_1)); double expected = Double.parseDouble(s); - System.out.println(expected + " == " + actual); +// System.out.println(expected + " == " + actual); assertEquals(expected, actual); }