Skip to content

Commit

Permalink
Fixes a bug that allowed NegativeArraySizeException to be thrown from…
Browse files Browse the repository at this point in the history
… IonReader.newBytes().
  • Loading branch information
tgregg committed Feb 2, 2024
1 parent 67ade97 commit 5305a78
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 10 deletions.
25 changes: 15 additions & 10 deletions src/main/java/com/amazon/ion/impl/IonCursorBinary.java
Original file line number Diff line number Diff line change
Expand Up @@ -898,17 +898,16 @@ private long calculateEndIndex_1_0(IonTypeID valueTid, boolean isAnnotated) {
}
byte b = buffer[(int) peekIndex++];
if (b < 0) {
endIndex = (b & LOWER_SEVEN_BITS_BITMASK);
endIndex = (b & LOWER_SEVEN_BITS_BITMASK) + peekIndex;
} else {
endIndex = uncheckedReadVarUInt_1_0(b);
endIndex = uncheckedReadVarUInt_1_0(b) + peekIndex;
if (endIndex < 0) {
throw new IonException("Unsupported value: declared length is too long.");
}
}
} else {
endIndex = valueTid.length;
endIndex = valueTid.length + peekIndex;
}
endIndex += peekIndex;
if (valueTid.type != null && valueTid.type.ordinal() >= LIST_TYPE_ORDINAL) {
event = Event.START_CONTAINER;
} else if (valueTid.isNopPad) {
Expand Down Expand Up @@ -1308,13 +1307,19 @@ private boolean slowReadValueHeader(IonTypeID valueTid, boolean isAnnotated, Mar
setCheckpoint(CheckpointLocation.AFTER_SCALAR_HEADER);
event = Event.START_SCALAR;
}
if (refillableState.isSkippingCurrentValue) {
// Any bytes that were skipped directly from the input must still be included in the logical endIndex so
// that the rest of the oversized value's bytes may be skipped.
endIndex = endIndex == DELIMITED_MARKER ? DELIMITED_MARKER : (peekIndex + valueLength + refillableState.individualBytesSkippedWithoutBuffering);
} else {
endIndex = endIndex == DELIMITED_MARKER ? DELIMITED_MARKER : (peekIndex + valueLength);
if (endIndex != DELIMITED_MARKER) {
if (refillableState.isSkippingCurrentValue) {
// Any bytes that were skipped directly from the input must still be included in the logical endIndex so
// that the rest of the oversized value's bytes may be skipped.
endIndex = peekIndex + valueLength + refillableState.individualBytesSkippedWithoutBuffering;
} else {
endIndex = peekIndex + valueLength;
}
if (endIndex < 0) {
throw new IonException("Unsupported value: declared length is too long.");
}
}

if (isAnnotated) {
validateAnnotationWrapperEndIndex(endIndex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,4 +480,20 @@ public void expectAttemptToParseNullDecimalAsJavaPrimitiveToFailCleanly() {
assertReaderThrowsForAllPrimitives(reader);
reader.close();
}

@ParameterizedTest(name = "constructFromBytes={0}")
@ValueSource(booleans = {true, false})
public void expectLobWithOverflowingEndIndexToFailCleanly(boolean constructFromBytes) {
// This test exercises the case where a value'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.
IonReaderContinuableCoreBinary reader = initializeReader(
constructFromBytes,
0xE0, 0x01, 0x00, 0xEA, // IVM
0xAE, // blob with length VarUInt to follow
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, // 9-byte VarUInt with value just below Long.MAX_VALUE
0xAF // The first byte of the blob
);
assertThrows(IonException.class, reader::nextValue);
reader.close();
}
}

0 comments on commit 5305a78

Please sign in to comment.