Skip to content

Commit

Permalink
Fix #1729
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jan 3, 2018
1 parent bdb5378 commit 6a1152c
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 21 deletions.
2 changes: 2 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,8 @@ Kevin Gallardo (newkek@github)
* Reported #1658: Infinite recursion when deserializing a class extending a Map,
with a recursive value type
(2.8.10)
* Reported #1729: Integer bounds verification when calling `TokenBuffer.getIntValue()`
(2.9.4)

Lukas Euler
* Reported #1735: Missing type checks when using polymorphic type ids
Expand Down
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Project: jackson-databind

2.9.4 (not yet released)

#1729: Integer bounds verification when calling `TokenBuffer.getIntValue()`
(reported by Kevin G)
#1854: NPE deserializing collection with `@JsonCreator` and `ACCEPT_CASE_INSENSITIVE_PROPERTIES`
(reported by rue-jw@github)
#1855: Blacklist for more serialization gadgets (dbcp/tomcat, spring)
Expand Down
89 changes: 84 additions & 5 deletions src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1573,16 +1573,22 @@ public float getFloatValue() throws IOException {
@Override
public int getIntValue() throws IOException
{
// optimize common case:
if (_currToken == JsonToken.VALUE_NUMBER_INT) {
return ((Number) _currentObject()).intValue();
Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
((Number) _currentObject()) : getNumberValue();
if ((n instanceof Integer) || _smallerThanInt(n)) {
return n.intValue();
}
return getNumberValue().intValue();
return _convertNumberToInt(n);
}

@Override
public long getLongValue() throws IOException {
return getNumberValue().longValue();
Number n = (_currToken == JsonToken.VALUE_NUMBER_INT) ?
((Number) _currentObject()) : getNumberValue();
if ((n instanceof Long) || _smallerThanLong(n)) {
return n.longValue();
}
return _convertNumberToLong(n);
}

@Override
Expand Down Expand Up @@ -1623,6 +1629,79 @@ public final Number getNumberValue() throws IOException {
+value.getClass().getName());
}

private final boolean _smallerThanInt(Number n) {
return (n instanceof Short) || (n instanceof Byte);
}

private final boolean _smallerThanLong(Number n) {
return (n instanceof Integer) || (n instanceof Short) || (n instanceof Byte);
}

/* 02-Jan-2017, tatu: Modified from method(s) in `ParserBase`
*/

protected int _convertNumberToInt(Number n) throws IOException
{
if (n instanceof Long) {
long l = n.longValue();
int result = (int) l;
if (((long) result) != l) {
reportOverflowInt();
}
return result;
}
if (n instanceof BigInteger) {
BigInteger big = (BigInteger) n;
if (BI_MIN_INT.compareTo(big) > 0
|| BI_MAX_INT.compareTo(big) < 0) {
reportOverflowInt();
}
} else if ((n instanceof Double) || (n instanceof Float)) {
double d = n.doubleValue();
// Need to check boundaries
if (d < MIN_INT_D || d > MAX_INT_D) {
reportOverflowInt();
}
return (int) d;
} else if (n instanceof BigDecimal) {
BigDecimal big = (BigDecimal) n;
if (BD_MIN_INT.compareTo(big) > 0
|| BD_MAX_INT.compareTo(big) < 0) {
reportOverflowInt();
}
} else {
_throwInternal();
}
return n.intValue();
}

protected long _convertNumberToLong(Number n) throws IOException
{
if (n instanceof BigInteger) {
BigInteger big = (BigInteger) n;
if (BI_MIN_LONG.compareTo(big) > 0
|| BI_MAX_LONG.compareTo(big) < 0) {
reportOverflowLong();
}
} else if ((n instanceof Double) || (n instanceof Float)) {
double d = n.doubleValue();
// Need to check boundaries
if (d < MIN_LONG_D || d > MAX_LONG_D) {
reportOverflowLong();
}
return (int) d;
} else if (n instanceof BigDecimal) {
BigDecimal big = (BigDecimal) n;
if (BD_MIN_LONG.compareTo(big) > 0
|| BD_MAX_LONG.compareTo(big) < 0) {
reportOverflowLong();
}
} else {
_throwInternal();
}
return n.longValue();
}

/*
/**********************************************************
/* Public API, access to token information, other
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ public class TestArrayConversions
final static String OVERFLOW_MSG_BYTE = "out of range of Java byte";
final static String OVERFLOW_MSG = "overflow";

final ObjectMapper mapper = new ObjectMapper();
final static String OVERFLOW_MSG_INT = "out of range of int";
final static String OVERFLOW_MSG_LONG = "out of range of long";

final ObjectMapper MAPPER = new ObjectMapper();

public void testNullXform() throws Exception
{
/* when given null, null should be returned without conversion
* (Java null has no type)
*/
assertNull(mapper.convertValue(null, Integer.class));
assertNull(mapper.convertValue(null, String.class));
assertNull(mapper.convertValue(null, byte[].class));
assertNull(MAPPER.convertValue(null, Integer.class));
assertNull(MAPPER.convertValue(null, String.class));
assertNull(MAPPER.convertValue(null, byte[].class));
}

/**
Expand Down Expand Up @@ -72,7 +75,7 @@ public void testIntArrayToX() throws Exception

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

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

List<Number> expNums = _numberList(data, data.length);
List<Long> actNums = mapper.convertValue(data, new TypeReference<List<Long>>() {});
List<Long> actNums = MAPPER.convertValue(data, new TypeReference<List<Long>>() {});
assertEquals(expNums, actNums);
}

public void testOverflows()
{
// Byte overflow
try {
mapper.convertValue(new int[] { 1000 }, byte[].class);
MAPPER.convertValue(new int[] { 1000 }, byte[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG_BYTE);
}
// Short overflow
try {
mapper.convertValue(new int[] { -99999 }, short[].class);
MAPPER.convertValue(new int[] { -99999 }, short[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG);
}
// Int overflow
try {
mapper.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
MAPPER.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG);
verifyException(e, OVERFLOW_MSG_INT);
}
// Longs need help of BigInteger...
BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE);
biggie.add(BigInteger.ONE);
List<BigInteger> l = new ArrayList<BigInteger>();
l.add(biggie);
try {
mapper.convertValue(l, int[].class);
MAPPER.convertValue(l, long[].class);
} catch (IllegalArgumentException e) {
verifyException(e, OVERFLOW_MSG);
verifyException(e, OVERFLOW_MSG_LONG);
}

}

/*
/********************************************************
/* Helper methods
Expand Down Expand Up @@ -171,7 +173,7 @@ private <T> T _convert(Object input, Class<T> outputType)
// must be a primitive array, like "int[].class"
if (!outputType.isArray()) throw new IllegalArgumentException();
if (!outputType.getComponentType().isPrimitive()) throw new IllegalArgumentException();
T result = mapper.convertValue(input, outputType);
T result = MAPPER.convertValue(input, outputType);
// sanity check first:
assertNotNull(result);
assertEquals(outputType, result.getClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.UUID;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.JsonParser.NumberType;
import com.fasterxml.jackson.core.io.SerializedString;
import com.fasterxml.jackson.core.util.JsonParserSequence;

Expand Down Expand Up @@ -124,7 +125,59 @@ public void testSimpleNumberWrites() throws IOException
p.close();
buf.close();
}


// [databind#1729]
public void testNumberOverflowInt() throws IOException
{
try (TokenBuffer buf = new TokenBuffer(null, false)) {
long big = 1L + Integer.MAX_VALUE;
buf.writeNumber(big);
try (JsonParser p = buf.asParser()) {
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(NumberType.LONG, p.getNumberType());
try {
p.getIntValue();
fail("Expected failure for `int` overflow");
} catch (JsonParseException e) {
verifyException(e, "Numeric value ("+big+") out of range of int");
}
}
}
// and ditto for coercion.
try (TokenBuffer buf = new TokenBuffer(null, false)) {
long big = 1L + Integer.MAX_VALUE;
buf.writeNumber(String.valueOf(big));
try (JsonParser p = buf.asParser()) {
// NOTE: oddity of buffering, no inspection of "real" type if given String...
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
try {
p.getIntValue();
fail("Expected failure for `int` overflow");
} catch (JsonParseException e) {
verifyException(e, "Numeric value ("+big+") out of range of int");
}
}
}
}

public void testNumberOverflowLong() throws IOException
{
try (TokenBuffer buf = new TokenBuffer(null, false)) {
BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
buf.writeNumber(big);
try (JsonParser p = buf.asParser()) {
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
try {
p.getLongValue();
fail("Expected failure for `long` overflow");
} catch (JsonParseException e) {
verifyException(e, "Numeric value ("+big+") out of range of long");
}
}
}
}

public void testParentContext() throws IOException
{
TokenBuffer buf = new TokenBuffer(null, false); // no ObjectCodec
Expand Down

0 comments on commit 6a1152c

Please sign in to comment.