Skip to content

Commit ac67e96

Browse files
committed
Bound checking BigDecimal in Timestamp to guard against DoS
Operations that require inflating the BigDecimal can be expensive for large numbers, examples: * longValue * intValue * setScale #159
1 parent cc146d6 commit ac67e96

File tree

2 files changed

+81
-12
lines changed

2 files changed

+81
-12
lines changed

src/software/amazon/ion/Timestamp.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ public final class Timestamp
8484
private static final int NO_SECONDS = 0;
8585
private static final BigDecimal NO_FRACTIONAL_SECONDS = null;
8686

87+
// 0001-01-01T00:00:00.0Z in millis
88+
private static final long MINIMUM_ALLOWED_TIMESTAMP_IN_MILLIS = -62135769600000L;
89+
90+
// 0001-01-01T00:00:00.0Z in millis
91+
static final BigDecimal MINIMUM_ALLOWED_FRACTIONAL_MILLIS = new BigDecimal(MINIMUM_ALLOWED_TIMESTAMP_IN_MILLIS);
92+
93+
// 9999-12-31T23:59:59.999999999999Z in millis
94+
static final BigDecimal MAXIMUM_ALLOWED_FRACTIONAL_MILLIS = new BigDecimal("253402300799999.999999999");
95+
96+
// determined empirically as a safe scale
97+
private static final int MAXIMUM_ALLOWED_MILLIS_SCALE = 100000;
98+
8799
/**
88100
* Unknown local offset from UTC.
89101
*/
@@ -266,9 +278,6 @@ private void apply_offset(int offset)
266278
}
267279
}
268280

269-
// 0001-01-01T00:00:00.0Z in millis
270-
private static final long MINIMUM_ALLOWED_TIMESTAMP_IN_MILLIS = -62135769600000L;
271-
272281
/**
273282
* This method uses deprecated methods from {@link java.util.Date}
274283
* instead of {@link Calendar} so that this code can be used (more easily)
@@ -704,6 +713,28 @@ private Timestamp(BigDecimal millis, Integer localOffset)
704713
{
705714
if (millis == null) throw new NullPointerException("millis is null");
706715

716+
// check bounds to avoid hanging when calling longValue() on decimals with large positive exponents,
717+
// e.g. 1e10000000
718+
if(millis.compareTo(MINIMUM_ALLOWED_FRACTIONAL_MILLIS) < 0 ||
719+
millis.compareTo(MAXIMUM_ALLOWED_FRACTIONAL_MILLIS) > 0) {
720+
throw new IllegalArgumentException("millis: " + millis + " is outside of valid the range: from "
721+
+ MINIMUM_ALLOWED_FRACTIONAL_MILLIS
722+
+ " to "
723+
+ MAXIMUM_ALLOWED_FRACTIONAL_MILLIS
724+
+ " both inclusive");
725+
}
726+
727+
// check scale size to avoid hanging on methods that need to inflate the fractional bigDecimal
728+
// Examples: toString and toMillis
729+
if(Math.abs(millis.scale()) >= MAXIMUM_ALLOWED_MILLIS_SCALE) {
730+
throw new IllegalArgumentException("millis: " + millis + " has a scale outside the valid range:"
731+
+ "from"
732+
+ -MAXIMUM_ALLOWED_MILLIS_SCALE
733+
+ "to"
734+
+ MAXIMUM_ALLOWED_MILLIS_SCALE
735+
+ "both inclusive");
736+
}
737+
707738
long ms = millis.longValue();
708739
set_fields_from_millis(ms);
709740

test/software/amazon/ion/TimestampTest.java

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import static software.amazon.ion.Decimal.NEGATIVE_ZERO;
1818
import static software.amazon.ion.Decimal.negativeZero;
19+
import static software.amazon.ion.Timestamp.MAXIMUM_ALLOWED_FRACTIONAL_MILLIS;
20+
import static software.amazon.ion.Timestamp.MINIMUM_ALLOWED_FRACTIONAL_MILLIS;
1921
import static software.amazon.ion.Timestamp.UNKNOWN_OFFSET;
2022
import static software.amazon.ion.Timestamp.UTC_OFFSET;
2123
import static software.amazon.ion.Timestamp.createFromUtcFields;
@@ -32,13 +34,8 @@
3234
import java.util.Calendar;
3335
import java.util.Date;
3436
import java.util.TimeZone;
37+
import org.junit.Ignore;
3538
import org.junit.Test;
36-
import software.amazon.ion.Decimal;
37-
import software.amazon.ion.IonTimestamp;
38-
import software.amazon.ion.IonType;
39-
import software.amazon.ion.IonValue;
40-
import software.amazon.ion.NullValueException;
41-
import software.amazon.ion.Timestamp;
4239
import software.amazon.ion.Timestamp.Precision;
4340

4441
/**
@@ -781,12 +778,53 @@ public void testNewTimestampFromBigDecimal()
781778
assertEquals("2012-01-01T12:12:30.555123Z", ts.toZString());
782779
}
783780

781+
@Test
782+
@Ignore // see https://github.com/amzn/ion-java/issues/160
783+
public void testNewTimestampFromMinimumAllowedMillis()
784+
{
785+
Timestamp ts = Timestamp.forMillis(MINIMUM_ALLOWED_FRACTIONAL_MILLIS, PST_OFFSET);
786+
assertEquals("0001-01-01T00:00:00.000Z", ts.toZString());
787+
}
788+
789+
@Test
790+
public void testNewTimestampFromBigDecimalWithMaximumAllowedMillis()
791+
{
792+
Timestamp ts = Timestamp.forMillis(MAXIMUM_ALLOWED_FRACTIONAL_MILLIS, PST_OFFSET);
793+
checkFields(9999, 12, 31, 15, 59, 59, new BigDecimal("0.999999999999"), PST_OFFSET, SECOND, ts);
794+
assertEquals("9999-12-31T15:59:59.999999999999-08:00", ts.toString());
795+
assertEquals("9999-12-31T23:59:59.999999999999Z", ts.toZString());
796+
}
784797

785-
@SuppressWarnings("unused")
786-
@Test (expected = NullPointerException.class)
798+
@Test(expected = NullPointerException.class)
787799
public void testNewTimestampFromBigDecimalWithNull()
788800
{
789-
Timestamp ts = Timestamp.forMillis(null, PST_OFFSET);
801+
Timestamp.forMillis(null, PST_OFFSET);
802+
}
803+
804+
@Test(expected = IllegalArgumentException.class)
805+
public void testNewTimestampFromBigDecimalWithMillisTooSmall()
806+
{
807+
// MIN - 1
808+
Timestamp.forMillis(MINIMUM_ALLOWED_FRACTIONAL_MILLIS.add(BigDecimal.ONE.negate()), PST_OFFSET);
809+
}
810+
811+
@Test(expected = IllegalArgumentException.class)
812+
public void testNewTimestampFromBigDecimalWithMillisTooBig()
813+
{
814+
// Max + 1
815+
Timestamp.forMillis(MAXIMUM_ALLOWED_FRACTIONAL_MILLIS.add(BigDecimal.ONE), PST_OFFSET);
816+
}
817+
818+
@Test(expected = IllegalArgumentException.class)
819+
public void testNewTimestampFromBigDecimalWithScaleTooBigPositive()
820+
{
821+
Timestamp.forMillis(new BigDecimal("1e100000"), PST_OFFSET);
822+
}
823+
824+
@Test(expected = IllegalArgumentException.class)
825+
public void testNewTimestampFromBigDecimalWithScaleTooBigNegative()
826+
{
827+
Timestamp.forMillis(new BigDecimal("1e-100000"), PST_OFFSET);
790828
}
791829

792830
/**

0 commit comments

Comments
 (0)