Skip to content

Commit 0d48020

Browse files
committed
Fix #834 (rare buffer condition for number-parsing, found by oss-fuzz project)
1 parent 2e2cff0 commit 0d48020

File tree

5 files changed

+99
-3
lines changed

5 files changed

+99
-3
lines changed

release-notes/VERSION-2.x

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ JSON library.
1414
=== Releases ===
1515
------------------------------------------------------------------------
1616

17+
Not yet released:
18+
19+
#834: ReaderBaseJsonParser._verifyRootSpace() can cause buffer boundary failure
20+
1721
2.14.0-rc3 (28-Oct-2022)
1822
2.14.0-rc2 (10-Oct-2022)
1923
2.14.0-rc1 (25-Sep-2022)

src/main/java/com/fasterxml/jackson/core/JsonEncoding.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public enum JsonEncoding {
2222
UTF32_BE("UTF-32BE", true, 32),
2323
UTF32_LE("UTF-32LE", false, 32)
2424
;
25-
25+
2626
private final String _javaName;
2727

2828
private final boolean _bigEndian;

src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -1811,7 +1811,13 @@ private final void _verifyRootSpace(int ch) throws IOException
18111811
case '\t':
18121812
return;
18131813
case '\r':
1814-
_skipCR();
1814+
// 29-Oct-2022, tatu: [core#834] requires change here, we MUST NOT
1815+
// force a read. As such let's simply push back the \r without
1816+
// further ado; it is enough to know there is valid WS separating
1817+
// NOTE: may need to revisit handling of plain \n to keep Location
1818+
// info more uniform. But has to do for now.
1819+
// _skipCR();
1820+
--_inputPtr;
18151821
return;
18161822
case '\n':
18171823
++_currInputRow;

src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -1768,7 +1768,10 @@ private final void _verifyRootSpace(int ch) throws IOException
17681768
case '\t':
17691769
return;
17701770
case '\r':
1771-
_skipCR();
1771+
// 29-Oct-2022, tatu: [core#834] While issue is only relevant for char-backed
1772+
// sources, let's unify handling to keep behavior uniform.
1773+
// _skipCR();
1774+
--_inputPtr;
17721775
return;
17731776
case '\n':
17741777
++_currInputRow;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.fasterxml.jackson.core.fuzz;
2+
3+
import java.io.*;
4+
import java.math.BigInteger;
5+
6+
import com.fasterxml.jackson.core.*;
7+
import com.fasterxml.jackson.core.exc.StreamReadException;
8+
import com.fasterxml.jackson.core.testsupport.ThrottledInputStream;
9+
10+
// Reproducing: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=52688
11+
// (reported as [core#834]
12+
public class Fuzz52688ParseTest extends BaseTest
13+
{
14+
private final JsonFactory JSON_F = new JsonFactory();
15+
16+
private final static BigInteger BIG_NUMBER = new BigInteger("3222"
17+
+"2222"
18+
+"2222"
19+
+"2222"
20+
+"222");
21+
22+
public void testBigNumberUTF16Parse() throws Exception
23+
{
24+
// 41 bytes as UTF16-LE; becomes 21 characters (last broken)
25+
final byte[] DOC = {
26+
0x33, 0, 0x32, 0, 0x32, 0, 0x32, 0,
27+
0x32, 0, 0x32, 0, 0x32, 0, 0x32, 0,
28+
0x32, 0, 0x32, 0, 0x32, 0, 0x32, 0,
29+
0x32, 0, 0x32, 0, 0x32, 0, 0x32, 0,
30+
0x32, 0, 0x32, 0, 0x32, 0, 0xd, 0,
31+
0x32
32+
};
33+
34+
try (JsonParser p = JSON_F.createParser(/*ObjectReadContext.empty(), */
35+
new ByteArrayInputStream(DOC))) {
36+
assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
37+
assertEquals(BIG_NUMBER, p.getBigIntegerValue());
38+
assertEquals(1, p.currentLocation().getLineNr());
39+
40+
// and now we should fail for the weird character
41+
try {
42+
JsonToken t = p.nextToken();
43+
fail("Should not pass, next token = "+t);
44+
} catch (StreamReadException e) {
45+
verifyException(e, "Unexpected character");
46+
assertEquals(2, p.currentLocation().getLineNr());
47+
assertEquals(2, e.getLocation().getLineNr());
48+
}
49+
}
50+
}
51+
52+
public void testBigNumberUTF8Parse() throws Exception
53+
{
54+
// Similar to UTF-16 case
55+
final byte[] DOC = {
56+
0x33, 0x32, 0x32, 0x32,
57+
0x32, 0x32, 0x32, 0x32,
58+
0x32, 0x32, 0x32, 0x32,
59+
0x32, 0x32, 0x32, 0x32,
60+
0x32, 0x32, 0x32, 0xd,
61+
(byte) '@'
62+
};
63+
64+
// Try to force buffer condition
65+
try (ThrottledInputStream in = new ThrottledInputStream(DOC, 1)) {
66+
try (JsonParser p = JSON_F.createParser(/*ObjectReadContext.empty(), */ in)) {
67+
assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken());
68+
assertEquals(BIG_NUMBER, p.getBigIntegerValue());
69+
assertEquals(1, p.currentLocation().getLineNr());
70+
71+
// and now we should fail for the weird character
72+
try {
73+
JsonToken t = p.nextToken();
74+
fail("Should not pass, next token = "+t);
75+
} catch (StreamReadException e) {
76+
verifyException(e, "Unexpected character");
77+
assertEquals(2, p.currentLocation().getLineNr());
78+
assertEquals(2, e.getLocation().getLineNr());
79+
}
80+
}
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)