Skip to content

Commit 6a1152c

Browse files
committed
Fix #1729
1 parent bdb5378 commit 6a1152c

File tree

5 files changed

+159
-21
lines changed

5 files changed

+159
-21
lines changed

release-notes/CREDITS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,8 @@ Kevin Gallardo (newkek@github)
643643
* Reported #1658: Infinite recursion when deserializing a class extending a Map,
644644
with a recursive value type
645645
(2.8.10)
646+
* Reported #1729: Integer bounds verification when calling `TokenBuffer.getIntValue()`
647+
(2.9.4)
646648

647649
Lukas Euler
648650
* Reported #1735: Missing type checks when using polymorphic type ids

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Project: jackson-databind
55

66
2.9.4 (not yet released)
77

8+
#1729: Integer bounds verification when calling `TokenBuffer.getIntValue()`
9+
(reported by Kevin G)
810
#1854: NPE deserializing collection with `@JsonCreator` and `ACCEPT_CASE_INSENSITIVE_PROPERTIES`
911
(reported by rue-jw@github)
1012
#1855: Blacklist for more serialization gadgets (dbcp/tomcat, spring)

src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,16 +1573,22 @@ public float getFloatValue() throws IOException {
15731573
@Override
15741574
public int getIntValue() throws IOException
15751575
{
1576-
// optimize common case:
1577-
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
1578-
return ((Number) _currentObject()).intValue();
1576+
Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
1577+
((Number) _currentObject()) : getNumberValue();
1578+
if ((n instanceof Integer) || _smallerThanInt(n)) {
1579+
return n.intValue();
15791580
}
1580-
return getNumberValue().intValue();
1581+
return _convertNumberToInt(n);
15811582
}
15821583

15831584
@Override
15841585
public long getLongValue() throws IOException {
1585-
return getNumberValue().longValue();
1586+
Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
1587+
((Number) _currentObject()) : getNumberValue();
1588+
if ((n instanceof Long) || _smallerThanLong(n)) {
1589+
return n.longValue();
1590+
}
1591+
return _convertNumberToLong(n);
15861592
}
15871593

15881594
@Override
@@ -1623,6 +1629,79 @@ public final Number getNumberValue() throws IOException {
16231629
+value.getClass().getName());
16241630
}
16251631

1632+
private final boolean _smallerThanInt(Number n) {
1633+
return (n instanceof Short) || (n instanceof Byte);
1634+
}
1635+
1636+
private final boolean _smallerThanLong(Number n) {
1637+
return (n instanceof Integer) || (n instanceof Short) || (n instanceof Byte);
1638+
}
1639+
1640+
/* 02-Jan-2017, tatu: Modified from method(s) in `ParserBase`
1641+
*/
1642+
1643+
protected int _convertNumberToInt(Number n) throws IOException
1644+
{
1645+
if (n instanceof Long) {
1646+
long l = n.longValue();
1647+
int result = (int) l;
1648+
if (((long) result) != l) {
1649+
reportOverflowInt();
1650+
}
1651+
return result;
1652+
}
1653+
if (n instanceof BigInteger) {
1654+
BigInteger big = (BigInteger) n;
1655+
if (BI_MIN_INT.compareTo(big) > 0
1656+
|| BI_MAX_INT.compareTo(big) < 0) {
1657+
reportOverflowInt();
1658+
}
1659+
} else if ((n instanceof Double) || (n instanceof Float)) {
1660+
double d = n.doubleValue();
1661+
// Need to check boundaries
1662+
if (d < MIN_INT_D || d > MAX_INT_D) {
1663+
reportOverflowInt();
1664+
}
1665+
return (int) d;
1666+
} else if (n instanceof BigDecimal) {
1667+
BigDecimal big = (BigDecimal) n;
1668+
if (BD_MIN_INT.compareTo(big) > 0
1669+
|| BD_MAX_INT.compareTo(big) < 0) {
1670+
reportOverflowInt();
1671+
}
1672+
} else {
1673+
_throwInternal();
1674+
}
1675+
return n.intValue();
1676+
}
1677+
1678+
protected long _convertNumberToLong(Number n) throws IOException
1679+
{
1680+
if (n instanceof BigInteger) {
1681+
BigInteger big = (BigInteger) n;
1682+
if (BI_MIN_LONG.compareTo(big) > 0
1683+
|| BI_MAX_LONG.compareTo(big) < 0) {
1684+
reportOverflowLong();
1685+
}
1686+
} else if ((n instanceof Double) || (n instanceof Float)) {
1687+
double d = n.doubleValue();
1688+
// Need to check boundaries
1689+
if (d < MIN_LONG_D || d > MAX_LONG_D) {
1690+
reportOverflowLong();
1691+
}
1692+
return (int) d;
1693+
} else if (n instanceof BigDecimal) {
1694+
BigDecimal big = (BigDecimal) n;
1695+
if (BD_MIN_LONG.compareTo(big) > 0
1696+
|| BD_MAX_LONG.compareTo(big) < 0) {
1697+
reportOverflowLong();
1698+
}
1699+
} else {
1700+
_throwInternal();
1701+
}
1702+
return n.longValue();
1703+
}
1704+
16261705
/*
16271706
/**********************************************************
16281707
/* Public API, access to token information, other

src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@ public class TestArrayConversions
1414
final static String OVERFLOW_MSG_BYTE = "out of range of Java byte";
1515
final static String OVERFLOW_MSG = "overflow";
1616

17-
final ObjectMapper mapper = new ObjectMapper();
17+
final static String OVERFLOW_MSG_INT = "out of range of int";
18+
final static String OVERFLOW_MSG_LONG = "out of range of long";
19+
20+
final ObjectMapper MAPPER = new ObjectMapper();
1821

1922
public void testNullXform() throws Exception
2023
{
2124
/* when given null, null should be returned without conversion
2225
* (Java null has no type)
2326
*/
24-
assertNull(mapper.convertValue(null, Integer.class));
25-
assertNull(mapper.convertValue(null, String.class));
26-
assertNull(mapper.convertValue(null, byte[].class));
27+
assertNull(MAPPER.convertValue(null, Integer.class));
28+
assertNull(MAPPER.convertValue(null, String.class));
29+
assertNull(MAPPER.convertValue(null, byte[].class));
2730
}
2831

2932
/**
@@ -72,7 +75,7 @@ public void testIntArrayToX() throws Exception
7275

7376
List<Number> expNums = _numberList(data, data.length);
7477
// Alas, due to type erasure, need to use TypeRef, not just class
75-
List<Integer> actNums = mapper.convertValue(data, new TypeReference<List<Integer>>() {});
78+
List<Integer> actNums = MAPPER.convertValue(data, new TypeReference<List<Integer>>() {});
7679
assertEquals(expNums, actNums);
7780
}
7881

@@ -84,43 +87,42 @@ public void testLongArrayToX() throws Exception
8487
verifyLongArrayConversion(data, int[].class);
8588

8689
List<Number> expNums = _numberList(data, data.length);
87-
List<Long> actNums = mapper.convertValue(data, new TypeReference<List<Long>>() {});
90+
List<Long> actNums = MAPPER.convertValue(data, new TypeReference<List<Long>>() {});
8891
assertEquals(expNums, actNums);
8992
}
9093

9194
public void testOverflows()
9295
{
9396
// Byte overflow
9497
try {
95-
mapper.convertValue(new int[] { 1000 }, byte[].class);
98+
MAPPER.convertValue(new int[] { 1000 }, byte[].class);
9699
} catch (IllegalArgumentException e) {
97100
verifyException(e, OVERFLOW_MSG_BYTE);
98101
}
99102
// Short overflow
100103
try {
101-
mapper.convertValue(new int[] { -99999 }, short[].class);
104+
MAPPER.convertValue(new int[] { -99999 }, short[].class);
102105
} catch (IllegalArgumentException e) {
103106
verifyException(e, OVERFLOW_MSG);
104107
}
105108
// Int overflow
106109
try {
107-
mapper.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
110+
MAPPER.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
108111
} catch (IllegalArgumentException e) {
109-
verifyException(e, OVERFLOW_MSG);
112+
verifyException(e, OVERFLOW_MSG_INT);
110113
}
111114
// Longs need help of BigInteger...
112115
BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE);
113116
biggie.add(BigInteger.ONE);
114117
List<BigInteger> l = new ArrayList<BigInteger>();
115118
l.add(biggie);
116119
try {
117-
mapper.convertValue(l, int[].class);
120+
MAPPER.convertValue(l, long[].class);
118121
} catch (IllegalArgumentException e) {
119-
verifyException(e, OVERFLOW_MSG);
122+
verifyException(e, OVERFLOW_MSG_LONG);
120123
}
121-
122124
}
123-
125+
124126
/*
125127
/********************************************************
126128
/* Helper methods
@@ -171,7 +173,7 @@ private <T> T _convert(Object input, Class<T> outputType)
171173
// must be a primitive array, like "int[].class"
172174
if (!outputType.isArray()) throw new IllegalArgumentException();
173175
if (!outputType.getComponentType().isPrimitive()) throw new IllegalArgumentException();
174-
T result = mapper.convertValue(input, outputType);
176+
T result = MAPPER.convertValue(input, outputType);
175177
// sanity check first:
176178
assertNotNull(result);
177179
assertEquals(outputType, result.getClass());

src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.UUID;
77

88
import com.fasterxml.jackson.core.*;
9+
import com.fasterxml.jackson.core.JsonParser.NumberType;
910
import com.fasterxml.jackson.core.io.SerializedString;
1011
import com.fasterxml.jackson.core.util.JsonParserSequence;
1112

@@ -124,7 +125,59 @@ public void testSimpleNumberWrites() throws IOException
124125
p.close();
125126
buf.close();
126127
}
127-
128+
129+
// [databind#1729]
130+
public void testNumberOverflowInt() throws IOException
131+
{
132+
try (TokenBuffer buf = new TokenBuffer(null, false)) {
133+
long big = 1L + Integer.MAX_VALUE;
134+
buf.writeNumber(big);
135+
try (JsonParser p = buf.asParser()) {
136+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
137+
assertEquals(NumberType.LONG, p.getNumberType());
138+
try {
139+
p.getIntValue();
140+
fail("Expected failure for `int` overflow");
141+
} catch (JsonParseException e) {
142+
verifyException(e, "Numeric value ("+big+") out of range of int");
143+
}
144+
}
145+
}
146+
// and ditto for coercion.
147+
try (TokenBuffer buf = new TokenBuffer(null, false)) {
148+
long big = 1L + Integer.MAX_VALUE;
149+
buf.writeNumber(String.valueOf(big));
150+
try (JsonParser p = buf.asParser()) {
151+
// NOTE: oddity of buffering, no inspection of "real" type if given String...
152+
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
153+
try {
154+
p.getIntValue();
155+
fail("Expected failure for `int` overflow");
156+
} catch (JsonParseException e) {
157+
verifyException(e, "Numeric value ("+big+") out of range of int");
158+
}
159+
}
160+
}
161+
}
162+
163+
public void testNumberOverflowLong() throws IOException
164+
{
165+
try (TokenBuffer buf = new TokenBuffer(null, false)) {
166+
BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
167+
buf.writeNumber(big);
168+
try (JsonParser p = buf.asParser()) {
169+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
170+
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
171+
try {
172+
p.getLongValue();
173+
fail("Expected failure for `long` overflow");
174+
} catch (JsonParseException e) {
175+
verifyException(e, "Numeric value ("+big+") out of range of long");
176+
}
177+
}
178+
}
179+
}
180+
128181
public void testParentContext() throws IOException
129182
{
130183
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec

0 commit comments

Comments
 (0)