diff --git a/tests/fuzz/Makefile b/tests/fuzz/Makefile index f96adcfaea7..16b2f7ae914 100644 --- a/tests/fuzz/Makefile +++ b/tests/fuzz/Makefile @@ -124,7 +124,8 @@ FUZZ_TARGETS := \ sequence_compression_api \ seekable_roundtrip \ huf_round_trip \ - huf_decompress + huf_decompress \ + frame_header_cross_format all: libregression.a $(FUZZ_TARGETS) @@ -238,6 +239,9 @@ huf_round_trip: $(FUZZ_HEADERS) $(FUZZ_ROUND_TRIP_OBJ) rt_fuzz_huf_round_trip.o huf_decompress: $(FUZZ_HEADERS) $(FUZZ_DECOMPRESS_OBJ) d_fuzz_huf_decompress.o $(CXX) $(FUZZ_TARGET_FLAGS) $(FUZZ_DECOMPRESS_OBJ) d_fuzz_huf_decompress.o $(LIB_FUZZING_ENGINE) -o $@ + +frame_header_cross_format: $(FUZZ_HEADERS) $(FUZZ_DECOMPRESS_OBJ) d_fuzz_frame_header_cross_format.o + $(CXX) $(FUZZ_TARGET_FLAGS) $(FUZZ_DECOMPRESS_OBJ) d_fuzz_frame_header_cross_format.o $(LIB_FUZZING_ENGINE) -o $@ libregression.a: $(FUZZ_HEADERS) $(PRGDIR)/util.h $(PRGDIR)/util.c d_fuzz_regression_driver.o $(AR) $(FUZZ_ARFLAGS) $@ d_fuzz_regression_driver.o diff --git a/tests/fuzz/frame_header_cross_format.c b/tests/fuzz/frame_header_cross_format.c new file mode 100644 index 00000000000..2f6b006dbb2 --- /dev/null +++ b/tests/fuzz/frame_header_cross_format.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +/** + * This fuzz target validates that ZSTD_getFrameHeader_advanced() is consistent between + * ZSTD_f_zstd1 format and ZSTD_f_zstd1_magicless format. + */ + +#include +#include +#include +#include "fuzz_helpers.h" +#define ZSTD_STATIC_LINKING_ONLY +#include "zstd.h" +#include "fuzz_data_producer.h" + +#include +void prettyPrint(ZSTD_frameHeader* header) { + printf("ZSTD_frameHeader:\n"); + printf(" frameContentSize: %llu\n", header->frameContentSize); + printf(" windowSize: %llu\n", header->windowSize); + printf(" blockSizeMax: %u\n", header->blockSizeMax); + printf(" frameType: %s\n", header->frameType == ZSTD_frame ? "ZSTD_frame" : "ZSTD_skippableFrame"); + printf(" headerSize: %u\n", header->headerSize); + printf(" dictID: %u\n", header->dictID); + printf(" checksumFlag: %u\n", header->checksumFlag); + printf(" _reserved1: %u\n", header->_reserved1); + printf(" _reserved2: %u\n", header->_reserved2); +} + +int LLVMFuzzerTestOneInput(const uint8_t *magiclessSrc, size_t magiclessSize) +{ + const int zstd_magic = ZSTD_MAGICNUMBER; + FUZZ_ASSERT(sizeof(zstd_magic) == 4); // assume sizeof(int) == 4 + const size_t standardSize = sizeof(zstd_magic) + magiclessSize; + void* standardSrc = FUZZ_malloc(standardSize); + memcpy(standardSrc, &zstd_magic, sizeof(zstd_magic)); // assume fuzzing on little-endian machine + memcpy(standardSrc + sizeof(zstd_magic), magiclessSrc, magiclessSize); + + ZSTD_frameHeader header_magicless; + ZSTD_frameHeader header_standard; + + // TODO: use fuzz data producer here + memset(&header_magicless, 0xAB, sizeof(ZSTD_frameHeader)); + memset(&header_standard, 0xCD, sizeof(ZSTD_frameHeader)); + + const size_t magicless_ret = ZSTD_getFrameHeader_advanced( + &header_magicless, magiclessSrc, magiclessSize, ZSTD_f_zstd1_magicless); + const size_t standard_ret = ZSTD_getFrameHeader_advanced( + &header_standard, standardSrc, standardSize, ZSTD_f_zstd1); + + // If magicless frame header is valid, then standard frame header should match + if (magicless_ret == 0) { + FUZZ_ASSERT(standard_ret == 0); + + // headerSize is not expected to be equal between formats + FUZZ_ASSERT(header_magicless.headerSize + sizeof(zstd_magic) == header_standard.headerSize); + + // Assert that all other fields are equal + header_magicless.headerSize = 0; + header_standard.headerSize = 0; + FUZZ_ASSERT(memcmp(&header_magicless, &header_standard, sizeof(ZSTD_frameHeader)) == 0); + } + + free(standardSrc); + return 0; +} diff --git a/tests/fuzz/fuzz.py b/tests/fuzz/fuzz.py index c489b8fa646..623cb747b36 100755 --- a/tests/fuzz/fuzz.py +++ b/tests/fuzz/fuzz.py @@ -65,6 +65,7 @@ def __init__(self, input_type, frame_type=FrameType.ZSTD): 'seekable_roundtrip': TargetInfo(InputType.RAW_DATA), 'huf_round_trip': TargetInfo(InputType.RAW_DATA), 'huf_decompress': TargetInfo(InputType.RAW_DATA), + 'frame_header_cross_format': TargetInfo(InputType.RAW_DATA), } TARGETS = list(TARGET_INFO.keys()) ALL_TARGETS = TARGETS + ['all']