Skip to content

Commit

Permalink
Adds a check for overflow when reading VarUInts.
Browse files Browse the repository at this point in the history
  • Loading branch information
tgregg committed Mar 15, 2024
1 parent 23a9346 commit d33087d
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/main/java/com/amazon/ion/impl/IonCursorBinary.java
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,9 @@ private long uncheckedReadVarUInt_1_0(byte currentByte) {
currentByte = buffer[(int) (peekIndex++)];
result = (result << VALUE_BITS_PER_VARUINT_BYTE) | (currentByte & LOWER_SEVEN_BITS_BITMASK);
} while (currentByte >= 0);
if (result < 0) {
throw new IonException("Found a VarUInt that was too large to fit in a `long`");
}
return result;
}

Expand Down Expand Up @@ -796,7 +799,7 @@ private boolean uncheckedReadAnnotationWrapperHeader_1_0(IonTypeID valueTid) {
}
endIndex += peekIndex;
setMarker(endIndex, valueMarker);
if (endIndex > limit) {
if (endIndex > limit || endIndex < 0) {
throw new IonException("Malformed data: declared length exceeds the number of bytes remaining in the stream.");
}
byte b = buffer[(int) peekIndex++];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,70 @@ public void expectLobWithOverflowingEndIndexToFailCleanly(boolean constructFromB
reader.close();
}

@Test
public void expectIncompleteAnnotationWrapperWithOverflowingEndIndexToFailCleanly() {
// This test exercises the case where an annotation wrapper's length itself does not overflow a java long,
// but its end index (calculated by adding the reader's current index in the buffer to the value's length) does.
// Note: this is only tested for fixed input sources because non-fixed sources will wait for more input
// to determine if the annotation wrapper is well-formed. The next test tests a similar failure case for
// both types of inputs.
IonReaderContinuableCoreBinary reader = initializeReader(
true,
0xE0, 0x01, 0x00, 0xEA,
0xEE,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE // 9-byte VarUInt with value just below Long.MAX_VALUE
);
assertThrows(IonException.class, reader::nextValue);
reader.close();
}

@ParameterizedTest(name = "constructFromBytes={0}")
@ValueSource(booleans = {true, false})
public void expectCompleteAnnotationWrapperWithOverflowingEndIndexToFailCleanly(boolean constructFromBytes) {
// This test exercises the case where an annotation wrapper's length itself does not overflow a java long,
// but its end index (calculated by adding the reader's current index in the buffer to the value's length) does.
// Unlike the previous test, this test contains the remaining bytes of the annotation wrapper, allowing the
// cursor to advance to a failure condition even when the input is not fixed.
IonReaderContinuableCoreBinary reader = initializeReader(
constructFromBytes,
0xE0, 0x01, 0x00, 0xEA,
0xEE,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, // 9-byte VarUInt with value just below Long.MAX_VALUE
0x81, 0x83
);
assertThrows(IonException.class, reader::nextValue);
reader.close();
}

@ParameterizedTest(name = "constructFromBytes={0}")
@ValueSource(booleans = {true, false})
public void expectLobWithOverflowingLengthToFailCleanly(boolean constructFromBytes) {
// This test exercises the case where a value's length overflows a java long.
IonReaderContinuableCoreBinary reader = initializeReader(
constructFromBytes,
0xE0, 0x01, 0x00, 0xEA, // IVM
0x9E, // clob with length VarUInt
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, // 10-byte VarUInt with value that exceeds Long.MAX_VALUE
0x00 // The first byte of the clob
);
assertThrows(IonException.class, reader::nextValue);
reader.close();
}

@ParameterizedTest(name = "constructFromBytes={0}")
@ValueSource(booleans = {true, false})
public void expectAnnotationWrapperWithOverflowingLengthToFailCleanly(boolean constructFromBytes) {
// This test exercises the case where an annotation wrapper's length overflows a java long.
IonReaderContinuableCoreBinary reader = initializeReader(
constructFromBytes,
0xE0, 0x01, 0x00, 0xEA,
0xEE,
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF // 10-byte VarUInt with value that exceeds Long.MAX_VALUE
);
assertThrows(IonException.class, reader::nextValue);
reader.close();
}

@Test
public void expectIncompleteContainerToFailCleanlyAfterFieldSid() {
IonReaderContinuableCoreBinary reader = initializeReader(
Expand Down

0 comments on commit d33087d

Please sign in to comment.