diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/deser/JacksonAvroParserImpl.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/deser/JacksonAvroParserImpl.java index 15c51549b..688704814 100644 --- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/deser/JacksonAvroParserImpl.java +++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/deser/JacksonAvroParserImpl.java @@ -614,9 +614,8 @@ private final String _finishShortText(int len) throws IOException _inputPtr += len; final byte[] inputBuf = _inputBuffer; - // Let's actually do a tight loop for ASCII first: final int end = inPtr + len; - + // Let's actually do a tight loop for ASCII first: int i; while ((i = inputBuf[inPtr]) >= 0) { outBuf[outPtr++] = (char) i; @@ -628,9 +627,18 @@ private final String _finishShortText(int len) throws IOException final int[] codes = sUtf8UnitLengths; do { i = inputBuf[inPtr++] & 0xFF; - switch (codes[i]) { - case 0: - break; + final int code = codes[i]; + if (code == 0) { // still optimized for ASCII + outBuf[outPtr++] = (char) i; + continue; + } + if ((inPtr + code) > end) { + if (code < 4) { + throw _constructError(String.format( + "Malformed %d-byte UTF-8 character at the end of Unicode text block", code)); + } + } + switch (code) { case 1: i = ((i & 0x1F) << 6) | (inputBuf[inPtr++] & 0x3F); break; @@ -650,7 +658,7 @@ private final String _finishShortText(int len) throws IOException i = 0xDC00 | (i & 0x3FF); break; default: // invalid - _reportError("Invalid byte "+Integer.toHexString(i)+" in Unicode text block"); + _reportError(String.format("Invalid byte 0x2X in Unicode text block", i)); } outBuf[outPtr++] = (char) i; } while (inPtr < end); diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_IOOBETest.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_IOOBETest.java new file mode 100644 index 000000000..fbcc2df6c --- /dev/null +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_IOOBETest.java @@ -0,0 +1,45 @@ +package com.fasterxml.jackson.dataformat.avro.fuzz; + +import org.junit.Test; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.exc.StreamReadException; +import com.fasterxml.jackson.dataformat.avro.AvroFactory; +import com.fasterxml.jackson.dataformat.avro.AvroMapper; +import com.fasterxml.jackson.dataformat.avro.AvroParser; +import com.fasterxml.jackson.dataformat.avro.AvroSchema; +import com.fasterxml.jackson.dataformat.avro.AvroTestBase; + +// [dataformats-binary#449] +public class AvroFuzz449_65618_IOOBETest extends AvroTestBase +{ + @JsonPropertyOrder({ "name", "value" }) + static class RootType { + public String name; + public int value; + } + + @Test + public void testFuzz65618IOOBE() throws Exception { + final AvroFactory factory = AvroFactory.builderWithApacheDecoder().build(); + final AvroMapper mapper = new AvroMapper(factory); + + final byte[] doc = { + (byte) 2, (byte) 22, (byte) 36, (byte) 2, (byte) 0, + (byte) 0, (byte) 8, (byte) 3, (byte) 3, (byte) 3, + (byte) 122, (byte) 3, (byte) -24 + }; + + final AvroSchema schema = mapper.schemaFor(RootType.class); + try (AvroParser p = factory.createParser(doc)) { + p.setSchema(schema); + assertToken(JsonToken.START_OBJECT, p.nextToken()); + assertToken(JsonToken.FIELD_NAME, p.nextToken()); + p.nextToken(); + fail("Should not pass (invalid content)"); + } catch (StreamReadException e) { + verifyException(e, "Malformed 2-byte UTF-8 character at the end of"); + } + } +} diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 8710ceb67..d354d3c0b 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -295,5 +295,9 @@ Arthur Chan (@arthurscchan) * Contributed #432 (ion) More methods from `IonReader` could throw an unexpected `AssertionError` (2.17.0) - * Contributed #434 (ion) Unexpected `NullPointerException` thrown from `IonParser::getNumberType()` + * Contributed #434: (ion) Unexpected `NullPointerException` thrown from + `IonParser::getNumberType()` + (2.17.0) + * Contributed #449: (avro) `IndexOutOfBoundsException` in `JacksonAvroParserImpl` + for invalid input (2.17.0) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 0123f86c4..e4856ef0a 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -31,6 +31,8 @@ Active maintainers: #434 (ion) Unexpected `NullPointerException` thrown from `IonParser::getNumberType()` (fix contributed by Arthur C) #437 (ion) `IonReader.next()` throws NPEs for some invalid content +#449 (avro) `IndexOutOfBoundsException` in `JacksonAvroParserImpl` for invalid input + (fix contributed by Arthur C) - (ion) Update `com.amazon.ion:ion-java` to 1.11.0 (from 1.10.5) 2.16.1 (24-Dec-2023)