1 /* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * All rights reserved. 4 * 5 * This source code is licensed under both the BSD-style license (found in the 6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 * in the COPYING file in the root directory of this source tree). 8 * You may select, at your option, one of the above-listed licenses. 9 */ 10 11 // This fuzz target validates decompression of magicless-format compressed data. 12 13 #include <stddef.h> 14 #include <stdlib.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include "fuzz_helpers.h" 18 #define ZSTD_STATIC_LINKING_ONLY 19 #include "zstd.h" 20 #include "fuzz_data_producer.h" 21 22 static ZSTD_DCtx *dctx = NULL; 23 24 int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size) 25 { 26 // Give a random portion of src data to the producer, to use for parameter generation. 27 // The rest will be interpreted as magicless compressed data. 28 FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size); 29 size_t magiclessSize = FUZZ_dataProducer_reserveDataPrefix(producer); 30 const uint8_t* const magiclessSrc = src; 31 size_t const dstSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size); 32 uint8_t* const standardDst = (uint8_t*)FUZZ_malloc(dstSize); 33 uint8_t* const magiclessDst = (uint8_t*)FUZZ_malloc(dstSize); 34 35 // Create standard-format src from magicless-format src 36 const uint32_t zstd_magic = ZSTD_MAGICNUMBER; 37 size_t standardSize = sizeof(zstd_magic) + magiclessSize; 38 uint8_t* const standardSrc = (uint8_t*)FUZZ_malloc(standardSize); 39 memcpy(standardSrc, &zstd_magic, sizeof(zstd_magic)); // assume fuzzing on little-endian machine 40 memcpy(standardSrc + sizeof(zstd_magic), magiclessSrc, magiclessSize); 41 42 // Truncate to a single frame 43 { 44 const size_t standardFrameCompressedSize = ZSTD_findFrameCompressedSize(standardSrc, standardSize); 45 if (ZSTD_isError(standardFrameCompressedSize)) { 46 goto cleanup_and_return; 47 } 48 standardSize = standardFrameCompressedSize; 49 magiclessSize = standardFrameCompressedSize - sizeof(zstd_magic); 50 } 51 52 // Create DCtx if needed 53 if (!dctx) { 54 dctx = ZSTD_createDCtx(); 55 FUZZ_ASSERT(dctx); 56 } 57 58 // Test one-shot decompression 59 { 60 FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters)); 61 FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1)); 62 const size_t standardRet = ZSTD_decompressDCtx( 63 dctx, standardDst, dstSize, standardSrc, standardSize); 64 65 FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters)); 66 FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1_magicless)); 67 const size_t magiclessRet = ZSTD_decompressDCtx( 68 dctx, magiclessDst, dstSize, magiclessSrc, magiclessSize); 69 70 // Standard accepts => magicless should accept 71 if (!ZSTD_isError(standardRet)) FUZZ_ZASSERT(magiclessRet); 72 73 // Magicless accepts => standard should accept 74 // NOTE: this is nice-to-have, please disable this check if it is difficult to satisfy. 75 if (!ZSTD_isError(magiclessRet)) FUZZ_ZASSERT(standardRet); 76 77 // If both accept, decompressed size and data should match 78 if (!ZSTD_isError(standardRet) && !ZSTD_isError(magiclessRet)) { 79 FUZZ_ASSERT(standardRet == magiclessRet); 80 if (standardRet > 0) { 81 FUZZ_ASSERT( 82 memcmp(standardDst, magiclessDst, standardRet) == 0 83 ); 84 } 85 } 86 } 87 88 // Test streaming decompression 89 { 90 ZSTD_inBuffer standardIn = { standardSrc, standardSize, 0 }; 91 ZSTD_inBuffer magiclessIn = { magiclessSrc, magiclessSize, 0 }; 92 ZSTD_outBuffer standardOut = { standardDst, dstSize, 0 }; 93 ZSTD_outBuffer magiclessOut = { magiclessDst, dstSize, 0 }; 94 95 FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters)); 96 FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1)); 97 const size_t standardRet = ZSTD_decompressStream(dctx, &standardOut, &standardIn); 98 99 FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters)); 100 FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1_magicless)); 101 const size_t magiclessRet = ZSTD_decompressStream(dctx, &magiclessOut, &magiclessIn); 102 103 // Standard accepts => magicless should accept 104 if (standardRet == 0) FUZZ_ASSERT(magiclessRet == 0); 105 106 // Magicless accepts => standard should accept 107 // NOTE: this is nice-to-have, please disable this check if it is difficult to satisfy. 108 if (magiclessRet == 0) FUZZ_ASSERT(standardRet == 0); 109 110 // If both accept, decompressed size and data should match 111 if (standardRet == 0 && magiclessRet == 0) { 112 FUZZ_ASSERT(standardOut.pos == magiclessOut.pos); 113 if (standardOut.pos > 0) { 114 FUZZ_ASSERT( 115 memcmp(standardOut.dst, magiclessOut.dst, standardOut.pos) == 0 116 ); 117 } 118 } 119 } 120 121 cleanup_and_return: 122 #ifndef STATEFUL_FUZZING 123 ZSTD_freeDCtx(dctx); dctx = NULL; 124 #endif 125 free(standardSrc); 126 free(standardDst); 127 free(magiclessDst); 128 FUZZ_dataProducer_free(producer); 129 return 0; 130 } 131