Skip to content

Commit b6c54cf

Browse files
committed
More work for #488, now handling coercions to double, float better to avoid drastic performance issue as per suggestions by @wujimin
1 parent a51fb6d commit b6c54cf

File tree

3 files changed

+152
-41
lines changed

3 files changed

+152
-41
lines changed

src/main/java/com/fasterxml/jackson/core/base/ParserBase.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -843,13 +843,18 @@ private void _parseSlowInt(int expType) throws IOException
843843
_numberLong = Long.parseLong(numStr);
844844
_numTypesValid = NR_LONG;
845845
} else {
846-
// 16-Oct-2018, tatu: Need to catch "too big" early due to... issues
846+
// 16-Oct-2018, tatu: Need to catch "too big" early due to [jackson-core#488]
847847
if ((expType == NR_INT) || (expType == NR_LONG)) {
848848
_reportTooLongInt(expType, numStr);
849849
}
850-
// nope, need the heavy guns... (rare case)
851-
_numberBigInt = new BigInteger(numStr);
852-
_numTypesValid = NR_BIGINT;
850+
if ((expType == NR_DOUBLE) || (expType == NR_FLOAT)) {
851+
_numberDouble = NumberInput.parseDouble(numStr);
852+
_numTypesValid = NR_DOUBLE;
853+
} else {
854+
// nope, need the heavy guns... (rare case)
855+
_numberBigInt = new BigInteger(numStr);
856+
_numTypesValid = NR_BIGINT;
857+
}
853858
}
854859
} catch (NumberFormatException nex) {
855860
// Can this ever occur? Due to overflow, maybe?
@@ -860,9 +865,16 @@ private void _parseSlowInt(int expType) throws IOException
860865
// @since 2.9.8
861866
protected void _reportTooLongInt(int expType, String rawNum) throws IOException
862867
{
863-
String numDesc = (rawNum.length() > 1000)
864-
? String.format("[Integer with %d digits]", rawNum.length())
865-
: rawNum;
868+
int rawLen = rawNum.length();
869+
final String numDesc;
870+
if (rawLen < 1000) {
871+
numDesc = rawNum;
872+
} else {
873+
if (rawNum.startsWith("-")) {
874+
rawLen -= 1;
875+
}
876+
numDesc = String.format("[Integer with %d digits]", rawLen);
877+
}
866878
_reportError("Numeric value (%s) out of range of %s", numDesc,
867879
(expType == NR_LONG) ? "long" : "int");
868880
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package com.fasterxml.jackson.core.read;
2+
3+
import java.math.BigInteger;
4+
5+
import com.fasterxml.jackson.core.*;
6+
7+
public class NumberOverflowTest
8+
extends com.fasterxml.jackson.core.BaseTest
9+
{
10+
private final JsonFactory FACTORY = new JsonFactory();
11+
12+
// NOTE: this should be long enough to trigger perf problems
13+
private final static int BIG_NUM_LEN = 199999;
14+
private final static String BIG_POS_INTEGER;
15+
static {
16+
StringBuilder sb = new StringBuilder(BIG_NUM_LEN);
17+
for (int i = 0; i < BIG_NUM_LEN; ++i) {
18+
sb.append('9');
19+
}
20+
BIG_POS_INTEGER = sb.toString();
21+
}
22+
23+
private final static String BIG_POS_DOC = "["+BIG_POS_INTEGER+"]";
24+
private final static String BIG_NEG_DOC = "[ -"+BIG_POS_INTEGER+"]";
25+
26+
public void testSimpleLongOverflow() throws Exception
27+
{
28+
BigInteger below = BigInteger.valueOf(Long.MIN_VALUE);
29+
below = below.subtract(BigInteger.ONE);
30+
BigInteger above = BigInteger.valueOf(Long.MAX_VALUE);
31+
above = above.add(BigInteger.ONE);
32+
33+
String DOC_BELOW = below.toString() + " ";
34+
String DOC_ABOVE = below.toString() + " ";
35+
36+
for (int mode : ALL_MODES) {
37+
JsonParser p = createParser(FACTORY, mode, DOC_BELOW);
38+
p.nextToken();
39+
try {
40+
long x = p.getLongValue();
41+
fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x);
42+
} catch (JsonParseException e) {
43+
verifyException(e, "out of range of long");
44+
}
45+
p.close();
46+
47+
p = createParser(mode, DOC_ABOVE);
48+
p.nextToken();
49+
try {
50+
long x = p.getLongValue();
51+
fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x);
52+
} catch (JsonParseException e) {
53+
verifyException(e, "out of range of long");
54+
}
55+
p.close();
56+
57+
}
58+
}
59+
60+
// Note: only 4 cardinal types; `short`, `byte` and `char` use same code paths
61+
// Note: due to [jackson-core#493], we'll skip DataInput-backed parser
62+
63+
// [jackson-core#488]
64+
public void testMaliciousLongOverflow() throws Exception
65+
{
66+
for (int mode : ALL_STREAMING_MODES) {
67+
for (String doc : new String[] { BIG_POS_DOC, BIG_NEG_DOC }) {
68+
JsonParser p = createParser(mode, doc);
69+
assertToken(JsonToken.START_ARRAY, p.nextToken());
70+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
71+
try {
72+
p.getLongValue();
73+
fail("Should not pass");
74+
} catch (JsonParseException e) {
75+
verifyException(e, "out of range of long");
76+
verifyException(e, "Integer with "+BIG_NUM_LEN+" digits");
77+
}
78+
p.close();
79+
}
80+
}
81+
}
82+
83+
// [jackson-core#488]
84+
public void testMaliciousIntOverflow() throws Exception
85+
{
86+
for (int mode : ALL_STREAMING_MODES) {
87+
for (String doc : new String[] { BIG_POS_DOC, BIG_NEG_DOC }) {
88+
JsonParser p = createParser(mode, doc);
89+
assertToken(JsonToken.START_ARRAY, p.nextToken());
90+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
91+
try {
92+
p.getIntValue();
93+
fail("Should not pass");
94+
} catch (JsonParseException e) {
95+
verifyException(e, "out of range of int");
96+
verifyException(e, "Integer with "+BIG_NUM_LEN+" digits");
97+
}
98+
p.close();
99+
}
100+
}
101+
}
102+
103+
// [jackson-core#488]
104+
public void testMaliciousBigIntToDouble() throws Exception
105+
{
106+
for (int mode : ALL_STREAMING_MODES) {
107+
final String doc = BIG_POS_DOC;
108+
JsonParser p = createParser(mode, doc);
109+
assertToken(JsonToken.START_ARRAY, p.nextToken());
110+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
111+
double d = p.getDoubleValue();
112+
assertEquals(Double.valueOf(BIG_POS_INTEGER), d);
113+
assertToken(JsonToken.END_ARRAY, p.nextToken());
114+
p.close();
115+
}
116+
}
117+
118+
// [jackson-core#488]
119+
public void testMaliciousBigIntToFloat() throws Exception
120+
{
121+
for (int mode : ALL_STREAMING_MODES) {
122+
final String doc = BIG_POS_DOC;
123+
JsonParser p = createParser(mode, doc);
124+
assertToken(JsonToken.START_ARRAY, p.nextToken());
125+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
126+
float f = p.getFloatValue();
127+
assertEquals(Float.valueOf(BIG_POS_INTEGER), f);
128+
assertToken(JsonToken.END_ARRAY, p.nextToken());
129+
p.close();
130+
}
131+
}
132+
}

src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public void testSimpleLong() throws Exception
108108
_testSimpleLong(MODE_READER);
109109
_testSimpleLong(MODE_DATA_INPUT);
110110
}
111-
111+
112112
private void _testSimpleLong(int mode) throws Exception
113113
{
114114
long EXP_L = 12345678907L;
@@ -309,39 +309,6 @@ private void _testNumbers(int mode) throws Exception
309309
p.close();
310310
}
311311

312-
public void testLongOverflow() throws Exception
313-
{
314-
BigInteger below = BigInteger.valueOf(Long.MIN_VALUE);
315-
below = below.subtract(BigInteger.ONE);
316-
BigInteger above = BigInteger.valueOf(Long.MAX_VALUE);
317-
above = above.add(BigInteger.ONE);
318-
319-
String DOC_BELOW = below.toString() + " ";
320-
String DOC_ABOVE = below.toString() + " ";
321-
322-
for (int mode : ALL_MODES) {
323-
JsonParser p = createParser(mode, DOC_BELOW);
324-
p.nextToken();
325-
try {
326-
long x = p.getLongValue();
327-
fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x);
328-
} catch (JsonParseException e) {
329-
verifyException(e, "out of range of long");
330-
}
331-
p.close();
332-
333-
p = createParser(mode, DOC_ABOVE);
334-
p.nextToken();
335-
try {
336-
long x = p.getLongValue();
337-
fail("Expected an exception for underflow (input "+p.getText()+"): instead, got long value: "+x);
338-
} catch (JsonParseException e) {
339-
verifyException(e, "out of range of long");
340-
}
341-
p.close();
342-
343-
}
344-
}
345312

346313
/**
347314
* Method that tries to test that number parsing works in cases where

0 commit comments

Comments
 (0)