From c0a056dbca00647d3b341922b8edfdc6da99ea30 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Tue, 9 Jan 2024 13:34:25 +0000 Subject: [PATCH 1/3] Additional fix for issue 449 Signed-off-by: Arthur Chan --- .../avro/deser/JacksonAvroParserImpl.java | 6 ++++ ...=> AvroFuzz449_65618_65649_IOOBETest.java} | 34 +++++++++++------- .../avro/fuzz/AvroFuzzTestUtil.java | 31 ++++++++++++++++ avro/src/test/resources/data/fuzz-65649.avro | Bin 0 -> 307 bytes 4 files changed, 59 insertions(+), 12 deletions(-) rename avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/{AvroFuzz449_65618_IOOBETest.java => AvroFuzz449_65618_65649_IOOBETest.java} (71%) create mode 100644 avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzzTestUtil.java create mode 100644 avro/src/test/resources/data/fuzz-65649.avro 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 688704814..c04545740 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 @@ -700,6 +700,12 @@ private final void _finishLongText(int len) throws IOException break; case 3: // 4-byte UTF c = _decodeUTF8_4(c); + // if outPtr >= outBuf.length before adding + // the input is malformed. + if (outPtr >= outBuf.length) { + throw _constructError(String.format( + "Malformed %d-byte UTF-8 character at the end of Unicode text block", code)); + } // Let's add first part right away: outBuf[outPtr++] = (char) (0xD800 | (c >> 10)); if (outPtr >= outBuf.length) { 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_65649_IOOBETest.java similarity index 71% rename from avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_IOOBETest.java rename to avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java index 6e55c8056..606d6385f 100644 --- 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_65649_IOOBETest.java @@ -12,7 +12,7 @@ import com.fasterxml.jackson.dataformat.avro.AvroTestBase; // [dataformats-binary#449] -public class AvroFuzz449_65618_IOOBETest extends AvroTestBase +public class AvroFuzz449_65618_65649_IOOBETest extends AvroTestBase { @JsonPropertyOrder({ "name", "value" }) static class RootType { @@ -20,26 +20,36 @@ static class RootType { public int value; } - @Test - public void testFuzz65618IOOBE() throws Exception { + private void testFuzzIOOBE(byte[] input, String msg) throws Exception { final AvroFactory factory = AvroFactory.builderWithNativeDecoder().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 = (AvroParser) mapper.createParser(doc)) { + try (AvroParser p = (AvroParser) mapper.createParser(input)) { p.setSchema(schema); assertToken(JsonToken.START_OBJECT, p.nextToken()); assertToken(JsonToken.FIELD_NAME, p.nextToken()); 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"); + verifyException(e, msg); } } + + @Test + public void testFuzz65618IOOBE() throws Exception { + 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 + }; + testFuzzIOOBE(doc, "Malformed 2-byte UTF-8 character at the end of"); +); + } + + @Test + public void testFuzz65649IOOBE() throws Exception { + final byte[] doc = AvroFuzzTestUtil.readResource("/data/fuzz-65649.avro"); + testFuzzIOOBE(doc, "Malformed 3-byte UTF-8 character at the end of"); + } } diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzzTestUtil.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzzTestUtil.java new file mode 100644 index 000000000..e7e6b30d4 --- /dev/null +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzzTestUtil.java @@ -0,0 +1,31 @@ +package com.fasterxml.jackson.dataformat.avro.fuzz; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public class AvroFuzzTestUtil +{ + public static byte[] readResource(String ref) + { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final byte[] buf = new byte[4000]; + + InputStream in = AvroFuzzTestUtil.class.getResourceAsStream(ref); + if (in != null) { + try { + int len; + while ((len = in.read(buf)) > 0) { + bytes.write(buf, 0, len); + } + in.close(); + } catch (IOException e) { + throw new RuntimeException("Failed to read resource '"+ref+"': "+e); + } + } + if (bytes.size() == 0) { + throw new IllegalArgumentException("Failed to read resource '"+ref+"': empty resource?"); + } + return bytes.toByteArray(); + } +} diff --git a/avro/src/test/resources/data/fuzz-65649.avro b/avro/src/test/resources/data/fuzz-65649.avro new file mode 100644 index 0000000000000000000000000000000000000000..78a1d3bcdb590bb849e768164b5641c0467029fa GIT binary patch literal 307 zcmZQ-(qW(i$e|RF Date: Tue, 9 Jan 2024 13:40:33 +0000 Subject: [PATCH 2/3] Fix bug Signed-off-by: Arthur Chan --- .../dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java index 606d6385f..e799475db 100644 --- a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java @@ -44,7 +44,6 @@ public void testFuzz65618IOOBE() throws Exception { (byte) 122, (byte) 3, (byte) -24 }; testFuzzIOOBE(doc, "Malformed 2-byte UTF-8 character at the end of"); -); } @Test From a9fbddd28b4246f89f7ccfd54f8c146da4d93efb Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 9 Jan 2024 13:56:19 -0800 Subject: [PATCH 3/3] Change proposed fix slightly --- .../dataformat/avro/deser/JacksonAvroParserImpl.java | 10 ++-------- .../avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java | 11 ++++++----- 2 files changed, 8 insertions(+), 13 deletions(-) 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 c04545740..07fb43bd2 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 @@ -700,19 +700,13 @@ private final void _finishLongText(int len) throws IOException break; case 3: // 4-byte UTF c = _decodeUTF8_4(c); - // if outPtr >= outBuf.length before adding - // the input is malformed. - if (outPtr >= outBuf.length) { - throw _constructError(String.format( - "Malformed %d-byte UTF-8 character at the end of Unicode text block", code)); - } - // Let's add first part right away: - outBuf[outPtr++] = (char) (0xD800 | (c >> 10)); if (outPtr >= outBuf.length) { outBuf = _textBuffer.finishCurrentSegment(); outPtr = 0; outEnd = outBuf.length; } + // Let's add first part right away: + outBuf[outPtr++] = (char) (0xD800 | (c >> 10)); c = 0xDC00 | (c & 0x3FF); // And let the other char output down below break; diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java index e799475db..c9fa0f5e0 100644 --- a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/fuzz/AvroFuzz449_65618_65649_IOOBETest.java @@ -20,9 +20,10 @@ static class RootType { public int value; } + final private AvroFactory factory = AvroFactory.builderWithNativeDecoder().build(); + final private AvroMapper mapper = new AvroMapper(factory); + private void testFuzzIOOBE(byte[] input, String msg) throws Exception { - final AvroFactory factory = AvroFactory.builderWithNativeDecoder().build(); - final AvroMapper mapper = new AvroMapper(factory); final AvroSchema schema = mapper.schemaFor(RootType.class); try (AvroParser p = (AvroParser) mapper.createParser(input)) { p.setSchema(schema); @@ -37,7 +38,7 @@ private void testFuzzIOOBE(byte[] input, String msg) throws Exception { } @Test - public void testFuzz65618IOOBE() throws Exception { + public void testFuzz65618_IOOBE() throws Exception { final byte[] doc = { (byte) 2, (byte) 22, (byte) 36, (byte) 2, (byte) 0, (byte) 0, (byte) 8, (byte) 3, (byte) 3, (byte) 3, @@ -47,8 +48,8 @@ public void testFuzz65618IOOBE() throws Exception { } @Test - public void testFuzz65649IOOBE() throws Exception { + public void testFuzz65649_IOOBE() throws Exception { final byte[] doc = AvroFuzzTestUtil.readResource("/data/fuzz-65649.avro"); - testFuzzIOOBE(doc, "Malformed 3-byte UTF-8 character at the end of"); + testFuzzIOOBE(doc, "Invalid UTF-8 start byte 0x80"); } }