Skip to content

Commit 1713faa

Browse files
committed
Fix #968: prevent some conversion from BigInteger to BigDecimal for perf reasons
1 parent 0de50fb commit 1713faa

File tree

4 files changed

+56
-6
lines changed

4 files changed

+56
-6
lines changed

release-notes/VERSION-2.x

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

17+
#968: Prevent inefficient internal conversion from `BigDecimal` to `BigInteger`
18+
wrt ultra-large scale
19+
1720
2.15.0-rc2 (28-Mar-2023)
1821

1922
#827: Add numeric value size limits via `StreamReadConstraints` (fixes

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

+38
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ public class StreamReadConstraints
4242
*/
4343
public static final int DEFAULT_MAX_STRING_LEN = 5_000_000;
4444

45+
/**
46+
* Limit for the maximum magnitude of Scale of {@link java.math.BigDecimal} that can be
47+
* converted to {@link java.math.BigInteger}; actual limit value is either
48+
* {@code 10 * maxNumLen} or this value, whichever is smaller.
49+
*<p>
50+
* "100k digits ought to be enough for anybody!"
51+
*/
52+
private static final int MAX_BIGINT_SCALE_MAGNITUDE = 100_000;
53+
4554
protected final int _maxNestingDepth;
4655
protected final int _maxNumLen;
4756
protected final int _maxStringLen;
@@ -283,4 +292,33 @@ public void validateStringLength(int length) throws StreamConstraintsException
283292
length, _maxStringLen));
284293
}
285294
}
295+
296+
/*
297+
/**********************************************************************
298+
/* Convenience methods for validation, other
299+
/**********************************************************************
300+
*/
301+
302+
/**
303+
* Convenience method that can be used to verify that a conversion to
304+
* {@link java.math.BigInteger}
305+
* {@link StreamConstraintsException}
306+
* is thrown.
307+
*
308+
* @param scale Scale (possibly negative) of {@link java.math.BigDecimal} to convert
309+
*
310+
* @throws StreamConstraintsException If magnitude (absolute value) of scale exceeds maximum
311+
* allowed
312+
*/
313+
public void validateBigIntegerScale(int scale) throws StreamConstraintsException
314+
{
315+
final int absScale = Math.abs(scale);
316+
final int limit = MAX_BIGINT_SCALE_MAGNITUDE;
317+
318+
if (absScale > limit) {
319+
throw new StreamConstraintsException(String.format(
320+
"BigDecimal scale (%d) magnitude exceeds maximum allowed (%d)",
321+
scale, limit));
322+
}
323+
}
286324
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,8 @@ protected void convertNumberToBigDecimal() throws IOException
12171217
// @since 2.15
12181218
protected BigInteger _convertBigDecimalToBigInteger(BigDecimal bigDec) throws IOException {
12191219
// 04-Apr-2022, tatu: wrt [core#968] Need to limit max scale magnitude
1220+
// (may throw StreamConstraintsException)
1221+
_streamReadConstraints.validateBigIntegerScale(bigDec.scale());
12201222
return bigDec.toBigInteger();
12211223
}
12221224

src/test/java/com/fasterxml/jackson/failing/PerfBigDecimalToInteger968.java

+13-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package com.fasterxml.jackson.failing;
22

3-
import java.math.BigInteger;
4-
53
import org.junit.Assert;
64
import org.junit.Test;
75

86
import com.fasterxml.jackson.core.*;
7+
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
98

109
// For [core#968]]
1110
public class PerfBigDecimalToInteger968
@@ -19,8 +18,12 @@ public void bigIntegerViaBigDecimal() throws Exception {
1918

2019
try (JsonParser p = JSON_F.createParser(DOC)) {
2120
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
22-
BigInteger value = p.getBigIntegerValue();
23-
Assert.assertNotNull(value);
21+
try {
22+
p.getBigIntegerValue();
23+
Assert.fail("Should not pass");
24+
} catch (StreamConstraintsException e) {
25+
Assert.assertEquals("BigDecimal scale (-25000000) magnitude exceeds maximum allowed (100000)", e.getMessage());
26+
}
2427
}
2528
}
2629

@@ -30,8 +33,12 @@ public void tinyIntegerViaBigDecimal() throws Exception {
3033

3134
try (JsonParser p = JSON_F.createParser(DOC)) {
3235
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
33-
BigInteger value = p.getBigIntegerValue();
34-
Assert.assertNotNull(value);
36+
try {
37+
p.getBigIntegerValue();
38+
Assert.fail("Should not pass");
39+
} catch (StreamConstraintsException e) {
40+
Assert.assertEquals("BigDecimal scale (25000000) magnitude exceeds maximum allowed (100000)", e.getMessage());
41+
}
3542
}
3643
}
3744

0 commit comments

Comments
 (0)