This is an automated email from the ASF dual-hosted git repository. iemejia pushed a commit to branch fuzzer in repository https://gitbox.apache.org/repos/asf/avro.git
commit 7ddcc9cd2bd44613109b6f91f30edf8744a7c5a8 Author: Ismaël Mejía <[email protected]> AuthorDate: Sat Apr 4 17:14:40 2026 +0000 Expand Java fuzz coverage for roundtrip paths Add roundtrip, typed projection, and single-object fuzz targets with valid corpora so Jazzer exercises successful encode/decode paths instead of only malformed-input handling. Normalize semantic comparisons in the fuzz harness to avoid false regressions from Avro's runtime representations. --- lang/java/fuzz/README.md | 15 +- lang/java/fuzz/REPORT.md | 12 +- .../java/org/apache/avro/fuzz/FuzzSupport.java | 161 +++++++++++++++++++++ .../org/apache/avro/fuzz/ProjectionFuzzer.java | 133 +++++++++++++++++ .../java/org/apache/avro/fuzz/RoundTripFuzzer.java | 128 ++++++++++++++++ .../org/apache/avro/fuzz/SeedCorpusGenerator.java | 108 ++++++++++++++ .../org/apache/avro/fuzz/SingleObjectFuzzer.java | 68 +++++++++ .../avro/fuzz/model/ReflectRoundTripRecord.java | 51 +++++++ .../avro/fuzz/model/SpecificProjectionEnum.java | 37 +++++ .../avro/fuzz/model/SpecificProjectionRecord.java | 125 ++++++++++++++++ .../fuzzDataFileReader/seed-valid-container.avro | Bin 516 -> 516 bytes .../seed-valid-container.avro | Bin 516 -> 516 bytes .../fuzzDataFileStream/seed-valid-container.avro | Bin 516 -> 516 bytes .../seed-valid-container.avro | Bin 516 -> 516 bytes .../fuzzReflectProjection/seed-valid-reflect.bin | Bin 0 -> 48 bytes .../fuzzSpecificProjection/seed-valid-specific.bin | Bin 0 -> 22 bytes .../fuzzBinaryRoundTrip/seed-valid-roundtrip.bin | Bin 0 -> 62 bytes .../crash-d84cec3ecd031ce35c724f6ee349f201d40a61f3 | Bin 0 -> 101 bytes .../seed-valid-roundtrip.avro | Bin 0 -> 873 bytes .../fuzzJsonRoundTrip/seed-valid-roundtrip.json | 1 + .../seed-valid-single-object.bin | Bin 0 -> 72 bytes 21 files changed, 834 insertions(+), 5 deletions(-) diff --git a/lang/java/fuzz/README.md b/lang/java/fuzz/README.md index 1b63bf8455..e2ff957c4b 100644 --- a/lang/java/fuzz/README.md +++ b/lang/java/fuzz/README.md @@ -11,14 +11,23 @@ This module hosts Jazzer-based fuzz tests for the Java Avro implementation. | `BinaryDecodingFuzzer` | `fuzzDirectBinaryDecoding` | Exercises direct binary decoding on `InputStream`-backed inputs | | `JsonDecodingFuzzer` | `fuzzJsonDecoding` | Exercises JSON decoding with unions, maps, arrays, and logical types | | `DataFileReaderFuzzer` | `fuzzDataFileReader` | Exercises seekable object container file parsing | +| `DataFileReaderFuzzer` | `fuzzDataFileReaderWithResolution` | Exercises seekable container parsing with explicit reader-schema resolution | | `DataFileReaderFuzzer` | `fuzzDataFileStream` | Exercises streaming object container file parsing | +| `DataFileReaderFuzzer` | `fuzzDataFileStreamWithResolution` | Exercises streaming container parsing with explicit reader-schema resolution | +| `RoundTripFuzzer` | `fuzzBinaryRoundTrip` | Roundtrips structured valid generic records through binary encoding | +| `RoundTripFuzzer` | `fuzzJsonRoundTrip` | Roundtrips structured valid generic records through JSON encoding | +| `RoundTripFuzzer` | `fuzzDataFileRoundTrip` | Roundtrips structured valid generic records through object container files | +| `ProjectionFuzzer` | `fuzzSpecificProjection` | Projects valid binary payloads through `SpecificDatumReader` | +| `ProjectionFuzzer` | `fuzzReflectProjection` | Projects valid binary payloads through `ReflectDatumReader` | +| `SingleObjectFuzzer` | `fuzzSingleObjectRoundTrip` | Roundtrips Avro single-object encoding with schema fingerprints | +| `SingleObjectFuzzer` | `fuzzSingleObjectDecoding` | Fuzzes malformed and valid single-object decoding | ## Design Notes - Targets only suppress exceptions that are expected from malformed Avro input. - Unexpected runtime failures are allowed to escape so Jazzer reports them. - The checked-in corpus includes both historical crash reproducers and valid seeds. -- Valid seeds help regression mode reach successful decode paths instead of replaying only prior failures. +- Valid seeds help regression mode reach successful encode/decode and projection paths instead of replaying only prior failures. ## Running @@ -26,7 +35,7 @@ Run from `lang/java/`: ```bash # Replay the checked-in regression corpus only. -mvn test -pl fuzz +mvn test -pl fuzz -am # Run continuous fuzzing for 60 seconds per target. mvn test -pl fuzz -Pfuzz @@ -35,7 +44,7 @@ mvn test -pl fuzz -Pfuzz mvn test -pl fuzz -Pfuzz -Dtest=BinaryDecodingFuzzer#fuzzDirectBinaryDecoding # Equivalent environment-variable toggle. -JAZZER_FUZZ=1 mvn test -pl fuzz +JAZZER_FUZZ=1 mvn test -pl fuzz -am ``` ## Corpus Layout diff --git a/lang/java/fuzz/REPORT.md b/lang/java/fuzz/REPORT.md index 36a70a5ca6..f4e47ace3d 100644 --- a/lang/java/fuzz/REPORT.md +++ b/lang/java/fuzz/REPORT.md @@ -4,7 +4,7 @@ `lang/java/fuzz/` is a Jazzer-based fuzzing module for the Java Avro implementation. -Current coverage includes 4 fuzz classes and 6 fuzz targets: +Current coverage includes parser, decoding, roundtrip, projection, and single-object targets: - schema parsing via `SchemaParser` - buffered binary decoding @@ -12,6 +12,9 @@ Current coverage includes 4 fuzz classes and 6 fuzz targets: - JSON decoding - seekable container-file decoding - streaming container-file decoding +- generic record roundtrips across binary, JSON, and object container formats +- typed projection through `SpecificDatumReader` and `ReflectDatumReader` +- Avro single-object encoding and decoding The module is wired into `lang/java/pom.xml` and can be run in regression or continuous-fuzzing mode through Maven. @@ -65,6 +68,9 @@ The fuzz module has been strengthened beyond the initial implementation: - successful binary and JSON decodes now require full input consumption - the direct binary target now uses a short-read stream to exercise hostile streaming behavior - container-file fuzzing now also covers reader-schema resolution paths +- roundtrip fuzzing now checks semantic equality across binary, JSON, and container formats +- Java model projection now covers both specific and reflect readers +- single-object message framing is now fuzzed directly ## BinaryDecoder Fix Notes @@ -86,6 +92,8 @@ Checked-in regression inputs now include: - valid JSON record seeds - valid binary datum seeds - valid object container file seeds for both reader and stream targets +- valid roundtrip binary, JSON, and container seeds +- valid specific, reflect, and single-object seeds This makes `mvn test -pl fuzz` useful as both a regression suite and a shallow successful-path smoke test. @@ -94,7 +102,7 @@ This makes `mvn test -pl fuzz` useful as both a regression suite and a shallow s Verified locally with: ```bash -mvn test -pl fuzz +mvn -Dmaven.build.cache.enabled=false test -pl fuzz -am ``` The module builds, replays the regression corpus, and runs all fuzz targets successfully. diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/FuzzSupport.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/FuzzSupport.java index e62e33d76f..ceafd41a09 100644 --- a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/FuzzSupport.java +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/FuzzSupport.java @@ -24,11 +24,21 @@ import org.apache.avro.Schema; import org.apache.avro.SchemaParseException; import org.apache.avro.SystemLimitException; import org.apache.avro.UnresolvedUnionException; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericEnumSymbol; +import org.apache.avro.generic.GenericFixed; +import org.apache.avro.generic.GenericRecord; import java.io.ByteArrayInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; final class FuzzSupport { static final Schema BINARY_WRITER_SCHEMA = new Schema.Parser() @@ -62,6 +72,20 @@ final class FuzzSupport { + "{\"name\":\"inner\",\"type\":{\"type\":\"record\",\"name\":\"Inner\",\"fields\":[" + "{\"name\":\"x\",\"type\":\"int\"}," + "{\"name\":\"y\",\"type\":\"int\"}]}}]}"); + static final Schema ROUND_TRIP_SCHEMA = new Schema.Parser() + .parse("{\"type\":\"record\",\"name\":\"RoundTripRoot\",\"fields\":[" + "{\"name\":\"id\",\"type\":\"long\"}," + + "{\"name\":\"name\",\"type\":\"string\"}," + + "{\"name\":\"createdDate\",\"type\":{\"type\":\"int\",\"logicalType\":\"date\"}}," + + "{\"name\":\"active\",\"type\":\"boolean\"}," + "{\"name\":\"score\",\"type\":\"double\"}," + + "{\"name\":\"payload\",\"type\":\"bytes\"}," + + "{\"name\":\"tags\",\"type\":{\"type\":\"array\",\"items\":\"string\"}}," + + "{\"name\":\"counts\",\"type\":{\"type\":\"map\",\"values\":\"long\"}}," + + "{\"name\":\"choice\",\"type\":[\"null\",\"string\",\"long\"],\"default\":null}," + + "{\"name\":\"hash\",\"type\":{\"type\":\"fixed\",\"name\":\"RoundTripHash\",\"size\":4}}," + + "{\"name\":\"inner\",\"type\":{\"type\":\"record\",\"name\":\"RoundTripInner\",\"fields\":[" + + "{\"name\":\"x\",\"type\":\"int\"}," + "{\"name\":\"y\",\"type\":\"int\"}]}}," + + "{\"name\":\"status\",\"type\":{\"type\":\"enum\",\"name\":\"RoundTripStatus\",\"symbols\":[\"NEW\",\"READY\",\"DONE\"]}}]}"); + private FuzzSupport() { } @@ -137,10 +161,147 @@ final class FuzzSupport { + "\"},\"extra\":null,\"inner\":{\"x\":" + x + ",\"y\":" + y + "}}"; } + static GenericRecord buildRoundTripRecord(FuzzedDataProvider data) { + GenericRecord record = new GenericData.Record(ROUND_TRIP_SCHEMA); + record.put("id", sanitizeLong(data.consumeLong())); + record.put("name", safeString(data.consumeString(24), "roundtrip")); + record.put("createdDate", data.consumeInt(0, 36500)); + record.put("active", data.consumeBoolean()); + record.put("score", boundedScore(data.consumeInt())); + record.put("payload", ByteBuffer.wrap(data.consumeBytes(data.consumeInt(0, 16)))); + record.put("tags", buildStringList(data)); + record.put("counts", buildCountMap(data)); + record.put("choice", buildUnionValue(data)); + record.put("hash", new GenericData.Fixed(ROUND_TRIP_SCHEMA.getField("hash").schema(), buildFixedBytes(data))); + record.put("inner", buildInnerRecord(data)); + record.put("status", new GenericData.EnumSymbol(ROUND_TRIP_SCHEMA.getField("status").schema(), + ROUND_TRIP_SCHEMA.getField("status").schema().getEnumSymbols().get(data.consumeInt(0, 2)))); + return record; + } + + static String safeString(String value, String fallback) { + return value.isEmpty() ? fallback : value; + } + + static boolean semanticEquals(Object left, Object right) { + return Objects.equals(normalizeDatum(left), normalizeDatum(right)); + } + + static boolean roundTripRecordsEqual(GenericRecord left, GenericRecord right) { + for (Schema.Field field : ROUND_TRIP_SCHEMA.getFields()) { + if (!semanticEquals(left.get(field.name()), right.get(field.name()))) { + return false; + } + } + return true; + } + private static double boundedScore(int value) { return (value % 1000) / 10.0d; } + private static GenericRecord buildInnerRecord(FuzzedDataProvider data) { + GenericRecord inner = new GenericData.Record(ROUND_TRIP_SCHEMA.getField("inner").schema()); + inner.put("x", data.consumeInt()); + inner.put("y", data.consumeInt()); + return inner; + } + + private static List<String> buildStringList(FuzzedDataProvider data) { + List<String> list = new ArrayList<>(); + int size = data.consumeInt(0, 4); + for (int i = 0; i < size; i++) { + list.add(safeString(data.consumeString(16), "tag" + i)); + } + return list; + } + + private static Map<String, Long> buildCountMap(FuzzedDataProvider data) { + Map<String, Long> map = new LinkedHashMap<>(); + int size = data.consumeInt(0, 4); + for (int i = 0; i < size; i++) { + map.put(safeString(toIdentifier(data.consumeString(12), "k" + i), "k" + i), sanitizeLong(data.consumeLong())); + } + return map; + } + + private static Object buildUnionValue(FuzzedDataProvider data) { + switch (data.consumeInt(0, 2)) { + case 0: + return null; + case 1: + return safeString(data.consumeString(16), "choice"); + default: + return sanitizeLong(data.consumeLong()); + } + } + + private static byte[] buildFixedBytes(FuzzedDataProvider data) { + byte[] bytes = data.consumeBytes(4); + if (bytes.length == 4) { + return bytes; + } + byte[] fixed = new byte[4]; + System.arraycopy(bytes, 0, fixed, 0, Math.min(bytes.length, fixed.length)); + return fixed; + } + + private static Object normalizeDatum(Object datum) { + if (datum == null) { + return null; + } + if (datum instanceof CharSequence) { + return datum.toString(); + } + if (datum instanceof GenericEnumSymbol<?> || datum instanceof Enum<?>) { + return datum.toString(); + } + if (datum instanceof ByteBuffer) { + return normalizeBytes((ByteBuffer) datum); + } + if (datum instanceof GenericFixed) { + return normalizeBytes(ByteBuffer.wrap(((GenericFixed) datum).bytes())); + } + if (datum instanceof GenericRecord) { + GenericRecord record = (GenericRecord) datum; + Map<String, Object> normalized = new LinkedHashMap<>(); + for (Schema.Field field : record.getSchema().getFields()) { + normalized.put(field.name(), normalizeDatum(record.get(field.name()))); + } + return normalized; + } + if (datum instanceof List<?>) { + List<Object> normalized = new ArrayList<>(); + for (Object value : (List<?>) datum) { + normalized.add(normalizeDatum(value)); + } + return normalized; + } + if (datum instanceof Map<?, ?>) { + Map<String, Object> normalized = new LinkedHashMap<>(); + for (Map.Entry<?, ?> entry : ((Map<?, ?>) datum).entrySet()) { + normalized.put(String.valueOf(normalizeDatum(entry.getKey())), normalizeDatum(entry.getValue())); + } + return normalized; + } + if (datum instanceof Double || datum instanceof Float) { + return ((Number) datum).doubleValue(); + } + if (datum instanceof Number) { + return ((Number) datum).longValue(); + } + return datum; + } + + private static List<Integer> normalizeBytes(ByteBuffer buffer) { + ByteBuffer copy = buffer.duplicate(); + List<Integer> bytes = new ArrayList<>(copy.remaining()); + while (copy.hasRemaining()) { + bytes.add(copy.get() & 0xff); + } + return bytes; + } + private static boolean isExpectedAvroRuntimeFailure(RuntimeException exception) { if (!(exception instanceof AvroRuntimeException)) { return false; diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/ProjectionFuzzer.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/ProjectionFuzzer.java new file mode 100644 index 0000000000..4068d38d42 --- /dev/null +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/ProjectionFuzzer.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro.fuzz; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; +import org.apache.avro.fuzz.model.ReflectRoundTripRecord; +import org.apache.avro.fuzz.model.SpecificProjectionEnum; +import org.apache.avro.fuzz.model.SpecificProjectionRecord; +import org.apache.avro.io.BinaryDecoder; +import org.apache.avro.io.BinaryEncoder; +import org.apache.avro.io.DecoderFactory; +import org.apache.avro.io.EncoderFactory; +import org.apache.avro.reflect.ReflectData; +import org.apache.avro.reflect.ReflectDatumReader; +import org.apache.avro.reflect.ReflectDatumWriter; +import org.apache.avro.specific.SpecificDatumReader; +import org.apache.avro.specific.SpecificDatumWriter; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +class ProjectionFuzzer { + private static final ReflectData REFLECT_DATA = ReflectData.get(); + + @FuzzTest + void fuzzSpecificProjection(FuzzedDataProvider data) throws IOException { + SpecificProjectionRecord record = buildSpecificRecord(data); + byte[] encoded = encodeSpecific(record); + + BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(encoded, null); + SpecificDatumReader<SpecificProjectionRecord> reader = new SpecificDatumReader<>(SpecificProjectionRecord.class); + SpecificProjectionRecord decoded = reader.read(null, decoder); + + if (!specificRecordsEqual(record, decoded)) { + throw new AssertionError("Specific projection changed semantic value"); + } + if (!decoder.isEnd()) { + throw new AssertionError("Specific projection left trailing bytes unread"); + } + } + + @FuzzTest + void fuzzReflectProjection(FuzzedDataProvider data) throws IOException { + ReflectRoundTripRecord record = buildReflectRecord(data); + byte[] encoded = encodeReflect(record); + + BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(encoded, null); + ReflectDatumReader<ReflectRoundTripRecord> reader = new ReflectDatumReader<>(ReflectRoundTripRecord.class); + ReflectRoundTripRecord decoded = reader.read(null, decoder); + + if (!record.equals(decoded)) { + throw new AssertionError("Reflect projection changed semantic value"); + } + if (!decoder.isEnd()) { + throw new AssertionError("Reflect projection left trailing bytes unread"); + } + } + + private static byte[] encodeSpecific(SpecificProjectionRecord record) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(output, null); + SpecificDatumWriter<SpecificProjectionRecord> writer = new SpecificDatumWriter<>(SpecificProjectionRecord.class); + writer.write(record, encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static byte[] encodeReflect(ReflectRoundTripRecord record) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(output, null); + ReflectDatumWriter<ReflectRoundTripRecord> writer = new ReflectDatumWriter<>(ReflectRoundTripRecord.class, + REFLECT_DATA); + writer.write(record, encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static SpecificProjectionRecord buildSpecificRecord(FuzzedDataProvider data) { + List<String> nicknames = new ArrayList<>(); + int nicknameCount = data.consumeInt(0, 3); + for (int i = 0; i < nicknameCount; i++) { + nicknames.add(FuzzSupport.safeString(data.consumeString(12), "nick" + i)); + } + + SpecificProjectionEnum typeEnum = data.consumeBoolean() ? null + : SpecificProjectionEnum.values()[data.consumeInt(0, SpecificProjectionEnum.values().length - 1)]; + return new SpecificProjectionRecord(data.consumeInt(), FuzzSupport.safeString(data.consumeString(24), "name"), + nicknames, typeEnum); + } + + private static ReflectRoundTripRecord buildReflectRecord(FuzzedDataProvider data) { + ReflectRoundTripRecord record = new ReflectRoundTripRecord(); + record.id = data.consumeLong(); + record.name = FuzzSupport.safeString(data.consumeString(24), "reflect"); + record.tags = new ArrayList<>(); + for (int i = 0, size = data.consumeInt(0, 3); i < size; i++) { + record.tags.add(FuzzSupport.safeString(data.consumeString(16), "tag" + i)); + } + record.counts = new LinkedHashMap<>(); + for (int i = 0, size = data.consumeInt(0, 3); i < size; i++) { + record.counts.put(FuzzSupport.safeString(data.consumeString(12), "k" + i), data.consumeLong()); + } + record.alias = data.consumeBoolean() ? null : FuzzSupport.safeString(data.consumeString(16), "alias"); + return record; + } + + private static boolean specificRecordsEqual(SpecificProjectionRecord left, SpecificProjectionRecord right) { + return FuzzSupport.semanticEquals(left.get(0), right.get(0)) + && FuzzSupport.semanticEquals(left.get(1), right.get(1)) + && FuzzSupport.semanticEquals(left.get(2), right.get(2)) + && FuzzSupport.semanticEquals(left.get(3), right.get(3)); + } +} diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/RoundTripFuzzer.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/RoundTripFuzzer.java new file mode 100644 index 0000000000..78417aa6f0 --- /dev/null +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/RoundTripFuzzer.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro.fuzz; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; +import org.apache.avro.file.DataFileReader; +import org.apache.avro.file.DataFileWriter; +import org.apache.avro.file.SeekableByteArrayInput; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericDatumReader; +import org.apache.avro.generic.GenericDatumWriter; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.BinaryDecoder; +import org.apache.avro.io.BinaryEncoder; +import org.apache.avro.io.DecoderFactory; +import org.apache.avro.io.EncoderFactory; +import org.apache.avro.io.JsonDecoder; +import org.apache.avro.io.JsonEncoder; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +class RoundTripFuzzer { + @FuzzTest + void fuzzBinaryRoundTrip(FuzzedDataProvider data) throws IOException { + GenericRecord record = FuzzSupport.buildRoundTripRecord(data); + byte[] encoded = encodeBinary(record); + + BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(encoded, null); + GenericDatumReader<GenericRecord> reader = new GenericDatumReader<>(FuzzSupport.ROUND_TRIP_SCHEMA); + GenericRecord decoded = reader.read(null, decoder); + + assertSemanticallyEqual(record, decoded); + if (!decoder.isEnd()) { + throw new AssertionError("Binary roundtrip left trailing bytes unread"); + } + } + + @FuzzTest + void fuzzJsonRoundTrip(FuzzedDataProvider data) throws IOException { + GenericRecord record = FuzzSupport.buildRoundTripRecord(data); + byte[] encoded = encodeJson(record); + + JsonDecoder decoder = DecoderFactory.get().jsonDecoder(FuzzSupport.ROUND_TRIP_SCHEMA, + new java.io.ByteArrayInputStream(encoded)); + GenericDatumReader<GenericRecord> reader = new GenericDatumReader<>(FuzzSupport.ROUND_TRIP_SCHEMA); + GenericRecord decoded = reader.read(null, decoder); + + assertSemanticallyEqual(record, decoded); + } + + @FuzzTest + void fuzzDataFileRoundTrip(FuzzedDataProvider data) throws IOException { + GenericRecord record = FuzzSupport.buildRoundTripRecord(data); + byte[] encoded = encodeContainer(record); + + try (DataFileReader<GenericRecord> reader = new DataFileReader<>(new SeekableByteArrayInput(encoded), + new GenericDatumReader<>(FuzzSupport.ROUND_TRIP_SCHEMA))) { + if (!reader.hasNext()) { + throw new AssertionError("Container roundtrip lost the encoded datum"); + } + GenericRecord decoded = reader.next(); + assertSemanticallyEqual(record, decoded); + if (reader.hasNext()) { + throw new AssertionError("Container roundtrip unexpectedly produced multiple data items"); + } + } + } + + private static byte[] encodeBinary(GenericRecord record) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(output, null); + GenericDatumWriter<GenericRecord> writer = new GenericDatumWriter<>(FuzzSupport.ROUND_TRIP_SCHEMA); + writer.write(record, encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static byte[] encodeJson(GenericRecord record) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + JsonEncoder encoder = EncoderFactory.get().jsonEncoder(FuzzSupport.ROUND_TRIP_SCHEMA, output); + GenericDatumWriter<GenericRecord> writer = new GenericDatumWriter<>(FuzzSupport.ROUND_TRIP_SCHEMA); + writer.write(record, encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static byte[] encodeContainer(GenericRecord record) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try (DataFileWriter<GenericRecord> writer = new DataFileWriter<>( + new GenericDatumWriter<>(FuzzSupport.ROUND_TRIP_SCHEMA))) { + writer.create(FuzzSupport.ROUND_TRIP_SCHEMA, output); + writer.append(record); + } + return output.toByteArray(); + } + + private static void assertSemanticallyEqual(GenericRecord expected, GenericRecord actual) { + if (!GenericData.get().validate(FuzzSupport.ROUND_TRIP_SCHEMA, actual)) { + throw new AssertionError("Decoded roundtrip record does not validate against the schema"); + } + if (!FuzzSupport.roundTripRecordsEqual(expected, actual)) { + throw new AssertionError("Roundtrip record changed semantic value"); + } + + GenericRecord copied = GenericData.get().deepCopy(FuzzSupport.ROUND_TRIP_SCHEMA, actual); + if (!FuzzSupport.roundTripRecordsEqual(actual, copied)) { + throw new AssertionError("Deep copy changed roundtrip record semantics"); + } + } +} diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/SeedCorpusGenerator.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/SeedCorpusGenerator.java index 4252317ea4..6776bc245c 100644 --- a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/SeedCorpusGenerator.java +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/SeedCorpusGenerator.java @@ -18,11 +18,19 @@ package org.apache.avro.fuzz; import org.apache.avro.file.DataFileWriter; +import org.apache.avro.fuzz.model.ReflectRoundTripRecord; +import org.apache.avro.fuzz.model.SpecificProjectionEnum; +import org.apache.avro.fuzz.model.SpecificProjectionRecord; import org.apache.avro.generic.GenericData; import org.apache.avro.generic.GenericDatumWriter; import org.apache.avro.generic.GenericRecord; import org.apache.avro.io.BinaryEncoder; import org.apache.avro.io.EncoderFactory; +import org.apache.avro.io.JsonEncoder; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.reflect.ReflectData; +import org.apache.avro.reflect.ReflectDatumWriter; +import org.apache.avro.specific.SpecificDatumWriter; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; @@ -49,6 +57,14 @@ class SeedCorpusGenerator { writeBytes("DataFileReaderFuzzerInputs/fuzzDataFileStream/seed-valid-container.avro", writeContainerFile()); writeBytes("DataFileReaderFuzzerInputs/fuzzDataFileStreamWithResolution/seed-valid-container.avro", writeContainerFile()); + writeBytes("RoundTripFuzzerInputs/fuzzBinaryRoundTrip/seed-valid-roundtrip.bin", writeRoundTripBinary()); + writeBytes("RoundTripFuzzerInputs/fuzzJsonRoundTrip/seed-valid-roundtrip.json", writeRoundTripJson()); + writeBytes("RoundTripFuzzerInputs/fuzzDataFileRoundTrip/seed-valid-roundtrip.avro", writeRoundTripContainer()); + writeBytes("ProjectionFuzzerInputs/fuzzSpecificProjection/seed-valid-specific.bin", + writeSpecificProjectionRecord()); + writeBytes("ProjectionFuzzerInputs/fuzzReflectProjection/seed-valid-reflect.bin", writeReflectProjectionRecord()); + writeBytes("SingleObjectFuzzerInputs/fuzzSingleObjectDecoding/seed-valid-single-object.bin", + writeSingleObjectRecord()); } private static byte[] writeBinaryRecord() throws IOException { @@ -79,6 +95,55 @@ class SeedCorpusGenerator { return output.toByteArray(); } + private static byte[] writeRoundTripBinary() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(output, null); + new GenericDatumWriter<GenericRecord>(FuzzSupport.ROUND_TRIP_SCHEMA).write(sampleRoundTripRecord(), encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static byte[] writeRoundTripJson() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + JsonEncoder encoder = EncoderFactory.get().jsonEncoder(FuzzSupport.ROUND_TRIP_SCHEMA, output); + new GenericDatumWriter<GenericRecord>(FuzzSupport.ROUND_TRIP_SCHEMA).write(sampleRoundTripRecord(), encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static byte[] writeRoundTripContainer() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try (DataFileWriter<GenericRecord> writer = new DataFileWriter<>( + new GenericDatumWriter<>(FuzzSupport.ROUND_TRIP_SCHEMA))) { + writer.create(FuzzSupport.ROUND_TRIP_SCHEMA, output); + writer.append(sampleRoundTripRecord()); + } + return output.toByteArray(); + } + + private static byte[] writeSpecificProjectionRecord() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(output, null); + new SpecificDatumWriter<SpecificProjectionRecord>(SpecificProjectionRecord.class).write(sampleSpecificRecord(), + encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static byte[] writeReflectProjectionRecord() throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(output, null); + new ReflectDatumWriter<ReflectRoundTripRecord>(ReflectRoundTripRecord.class, ReflectData.AllowNull.get()) + .write(sampleReflectRecord(), encoder); + encoder.flush(); + return output.toByteArray(); + } + + private static byte[] writeSingleObjectRecord() throws IOException { + return toArray( + new BinaryMessageEncoder<>(GenericData.get(), FuzzSupport.ROUND_TRIP_SCHEMA).encode(sampleRoundTripRecord())); + } + private static GenericRecord sampleBinaryRecord() { GenericRecord nodeTail = new GenericData.Record(NODE_SCHEMA); nodeTail.put("value", "tail"); @@ -115,6 +180,49 @@ class SeedCorpusGenerator { return record; } + private static GenericRecord sampleRoundTripRecord() { + GenericRecord record = new GenericData.Record(FuzzSupport.ROUND_TRIP_SCHEMA); + record.put("id", 19L); + record.put("name", "roundtrip"); + record.put("createdDate", 42); + record.put("active", true); + record.put("score", 12.3d); + record.put("payload", java.nio.ByteBuffer.wrap("blob".getBytes(StandardCharsets.UTF_8))); + record.put("tags", java.util.List.of("alpha", "beta")); + record.put("counts", java.util.Map.of("x", 1L, "y", 2L)); + record.put("choice", "choice"); + record.put("hash", + new GenericData.Fixed(FuzzSupport.ROUND_TRIP_SCHEMA.getField("hash").schema(), new byte[] { 4, 3, 2, 1 })); + GenericRecord inner = new GenericData.Record(FuzzSupport.ROUND_TRIP_SCHEMA.getField("inner").schema()); + inner.put("x", 7); + inner.put("y", 8); + record.put("inner", inner); + record.put("status", + new GenericData.EnumSymbol(FuzzSupport.ROUND_TRIP_SCHEMA.getField("status").schema(), "READY")); + return record; + } + + private static SpecificProjectionRecord sampleSpecificRecord() { + return new SpecificProjectionRecord(9, "specific", java.util.List.of("one", "two"), SpecificProjectionEnum.BETA); + } + + private static ReflectRoundTripRecord sampleReflectRecord() { + ReflectRoundTripRecord record = new ReflectRoundTripRecord(); + record.id = 5L; + record.name = "reflect"; + record.tags = java.util.List.of("first", "second"); + record.counts = java.util.Map.of("left", 1L, "right", 2L); + record.alias = "seen"; + return record; + } + + private static byte[] toArray(java.nio.ByteBuffer buffer) { + java.nio.ByteBuffer copy = buffer.duplicate(); + byte[] bytes = new byte[copy.remaining()]; + copy.get(bytes); + return bytes; + } + private static void writeBytes(String relativePath, byte[] data) throws IOException { Path output = RESOURCE_ROOT.resolve(relativePath); Files.createDirectories(output.getParent()); diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/SingleObjectFuzzer.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/SingleObjectFuzzer.java new file mode 100644 index 0000000000..c122e4bc67 --- /dev/null +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/SingleObjectFuzzer.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro.fuzz; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; +import com.code_intelligence.jazzer.junit.FuzzTest; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.message.BadHeaderException; +import org.apache.avro.message.BinaryMessageDecoder; +import org.apache.avro.message.BinaryMessageEncoder; +import org.apache.avro.message.MissingSchemaException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +class SingleObjectFuzzer { + @FuzzTest + void fuzzSingleObjectRoundTrip(FuzzedDataProvider data) throws IOException { + GenericRecord record = FuzzSupport.buildRoundTripRecord(data); + BinaryMessageEncoder<GenericRecord> encoder = new BinaryMessageEncoder<>(GenericData.get(), + FuzzSupport.ROUND_TRIP_SCHEMA); + BinaryMessageDecoder<GenericRecord> decoder = new BinaryMessageDecoder<>(GenericData.get(), + FuzzSupport.ROUND_TRIP_SCHEMA); + + ByteBuffer encoded = encoder.encode(record); + GenericRecord decoded = decoder.decode(encoded); + + if (!FuzzSupport.roundTripRecordsEqual(record, decoded)) { + throw new AssertionError("Single-object roundtrip changed semantic value"); + } + } + + @FuzzTest + void fuzzSingleObjectDecoding(byte[] data) throws IOException { + BinaryMessageDecoder<GenericRecord> decoder = new BinaryMessageDecoder<>(GenericData.get(), + FuzzSupport.ROUND_TRIP_SCHEMA); + try (ByteArrayInputStream input = new ByteArrayInputStream(data)) { + decoder.decode(input, null); + if (input.available() > 0) { + throw new AssertionError("Single-object decoder left trailing bytes unread"); + } + } catch (BadHeaderException | MissingSchemaException e) { + // Expected for malformed or unknown single-object payloads. + } catch (RuntimeException e) { + if (!FuzzSupport.isExpectedDecodingFailure(e)) { + throw e; + } + } + } +} diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/ReflectRoundTripRecord.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/ReflectRoundTripRecord.java new file mode 100644 index 0000000000..a5750ec532 --- /dev/null +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/ReflectRoundTripRecord.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro.fuzz.model; + +import org.apache.avro.reflect.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class ReflectRoundTripRecord { + public long id; + public String name; + public List<String> tags; + public Map<String, Long> counts; + @Nullable + public String alias; + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof ReflectRoundTripRecord)) { + return false; + } + ReflectRoundTripRecord that = (ReflectRoundTripRecord) other; + return id == that.id && Objects.equals(name, that.name) && Objects.equals(tags, that.tags) + && Objects.equals(counts, that.counts) && Objects.equals(alias, that.alias); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, tags, counts, alias); + } +} diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/SpecificProjectionEnum.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/SpecificProjectionEnum.java new file mode 100644 index 0000000000..af5e453e96 --- /dev/null +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/SpecificProjectionEnum.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro.fuzz.model; + +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericEnumSymbol; + +public enum SpecificProjectionEnum implements GenericEnumSymbol<SpecificProjectionEnum> { + ALPHA, BETA, GAMMA; + + public static final Schema SCHEMA$ = new Schema.Parser().parse( + "{\"type\":\"enum\",\"name\":\"SpecificProjectionEnum\",\"namespace\":\"org.apache.avro.fuzz.model\",\"symbols\":[\"ALPHA\",\"BETA\",\"GAMMA\"]}"); + + public static Schema getClassSchema() { + return SCHEMA$; + } + + @Override + public Schema getSchema() { + return SCHEMA$; + } +} diff --git a/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/SpecificProjectionRecord.java b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/SpecificProjectionRecord.java new file mode 100644 index 0000000000..3e0aa4d12f --- /dev/null +++ b/lang/java/fuzz/src/test/java/org/apache/avro/fuzz/model/SpecificProjectionRecord.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro.fuzz.model; + +import org.apache.avro.AvroRuntimeException; +import org.apache.avro.Schema; +import org.apache.avro.specific.SpecificRecord; +import org.apache.avro.specific.SpecificRecordBase; + +import java.util.List; +import java.util.ArrayList; +import java.util.Objects; + +public class SpecificProjectionRecord extends SpecificRecordBase implements SpecificRecord { + public static final Schema SCHEMA$ = new Schema.Parser().parse( + "{\"type\":\"record\",\"name\":\"SpecificProjectionRecord\",\"namespace\":\"org.apache.avro.fuzz.model\",\"fields\":[" + + "{\"name\":\"id\",\"type\":\"int\"}," + "{\"name\":\"name\",\"type\":\"string\"}," + + "{\"name\":\"tags\",\"type\":{\"type\":\"array\",\"items\":\"string\"}}," + + "{\"name\":\"status\",\"type\":[\"null\",{\"type\":\"enum\",\"name\":\"SpecificProjectionEnum\",\"symbols\":[\"ALPHA\",\"BETA\",\"GAMMA\"]}],\"default\":null}]}"); + + private int id; + private String name; + private List<String> tags; + private SpecificProjectionEnum status; + + public SpecificProjectionRecord() { + } + + public SpecificProjectionRecord(int id, String name, List<String> tags, SpecificProjectionEnum status) { + this.id = id; + this.name = name; + this.tags = tags; + this.status = status; + } + + public static Schema getClassSchema() { + return SCHEMA$; + } + + @Override + public Schema getSchema() { + return SCHEMA$; + } + + @Override + public Object get(int field) { + switch (field) { + case 0: + return id; + case 1: + return name; + case 2: + return tags; + case 3: + return status; + default: + throw new AvroRuntimeException("Bad index: " + field); + } + } + + @SuppressWarnings("unchecked") + @Override + public void put(int field, Object value) { + switch (field) { + case 0: + id = (Integer) value; + break; + case 1: + name = value == null ? null : value.toString(); + break; + case 2: + tags = normalizeStrings((List<?>) value); + break; + case 3: + status = (SpecificProjectionEnum) value; + break; + default: + throw new AvroRuntimeException("Bad index: " + field); + } + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof SpecificProjectionRecord)) { + return false; + } + SpecificProjectionRecord that = (SpecificProjectionRecord) other; + return id == that.id && Objects.equals(name, that.name) && Objects.equals(tags, that.tags) && status == that.status; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, tags, status); + } + + private static List<String> normalizeStrings(List<?> values) { + if (values == null) { + return null; + } + + List<String> normalized = new ArrayList<>(values.size()); + for (Object value : values) { + normalized.add(value == null ? null : value.toString()); + } + return normalized; + } +} diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReader/seed-valid-container.avro b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReader/seed-valid-container.avro index 4d36604052..aac8a615a1 100644 Binary files a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReader/seed-valid-container.avro and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReader/seed-valid-container.avro differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReaderWithResolution/seed-valid-container.avro b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReaderWithResolution/seed-valid-container.avro index 0a1867af3f..2ede2a00ef 100644 Binary files a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReaderWithResolution/seed-valid-container.avro and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileReaderWithResolution/seed-valid-container.avro differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStream/seed-valid-container.avro b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStream/seed-valid-container.avro index a7b94e7500..f83e6178d3 100644 Binary files a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStream/seed-valid-container.avro and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStream/seed-valid-container.avro differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStreamWithResolution/seed-valid-container.avro b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStreamWithResolution/seed-valid-container.avro index b10d4dfe32..ee00c33089 100644 Binary files a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStreamWithResolution/seed-valid-container.avro and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/DataFileReaderFuzzerInputs/fuzzDataFileStreamWithResolution/seed-valid-container.avro differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/ProjectionFuzzerInputs/fuzzReflectProjection/seed-valid-reflect.bin b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/ProjectionFuzzerInputs/fuzzReflectProjection/seed-valid-reflect.bin new file mode 100644 index 0000000000..0242ee9bfa Binary files /dev/null and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/ProjectionFuzzerInputs/fuzzReflectProjection/seed-valid-reflect.bin differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/ProjectionFuzzerInputs/fuzzSpecificProjection/seed-valid-specific.bin b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/ProjectionFuzzerInputs/fuzzSpecificProjection/seed-valid-specific.bin new file mode 100644 index 0000000000..8b6dbb7a9b Binary files /dev/null and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/ProjectionFuzzerInputs/fuzzSpecificProjection/seed-valid-specific.bin differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzBinaryRoundTrip/seed-valid-roundtrip.bin b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzBinaryRoundTrip/seed-valid-roundtrip.bin new file mode 100644 index 0000000000..19f0ea0c2c Binary files /dev/null and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzBinaryRoundTrip/seed-valid-roundtrip.bin differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzDataFileRoundTrip/crash-d84cec3ecd031ce35c724f6ee349f201d40a61f3 b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzDataFileRoundTrip/crash-d84cec3ecd031ce35c724f6ee349f201d40a61f3 new file mode 100644 index 0000000000..7a1a338ec2 Binary files /dev/null and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzDataFileRoundTrip/crash-d84cec3ecd031ce35c724f6ee349f201d40a61f3 differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzDataFileRoundTrip/seed-valid-roundtrip.avro b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzDataFileRoundTrip/seed-valid-roundtrip.avro new file mode 100644 index 0000000000..9ca7433e0d Binary files /dev/null and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzDataFileRoundTrip/seed-valid-roundtrip.avro differ diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzJsonRoundTrip/seed-valid-roundtrip.json b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzJsonRoundTrip/seed-valid-roundtrip.json new file mode 100644 index 0000000000..e0ba1505a5 --- /dev/null +++ b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/RoundTripFuzzerInputs/fuzzJsonRoundTrip/seed-valid-roundtrip.json @@ -0,0 +1 @@ +{"id":19,"name":"roundtrip","createdDate":42,"active":true,"score":12.3,"payload":"blob","tags":["alpha","beta"],"counts":{"y":2,"x":1},"choice":{"string":"choice"},"hash":"\u0004\u0003\u0002\u0001","inner":{"x":7,"y":8},"status":"READY"} \ No newline at end of file diff --git a/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/SingleObjectFuzzerInputs/fuzzSingleObjectDecoding/seed-valid-single-object.bin b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/SingleObjectFuzzerInputs/fuzzSingleObjectDecoding/seed-valid-single-object.bin new file mode 100644 index 0000000000..495bf1e6b0 Binary files /dev/null and b/lang/java/fuzz/src/test/resources/org/apache/avro/fuzz/SingleObjectFuzzerInputs/fuzzSingleObjectDecoding/seed-valid-single-object.bin differ
