while (srcSize >= ZSTD_startingInputLength(dctx->format)) {
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
- if (ZSTD_isLegacy(src, srcSize)) {
+ if (dctx->format == ZSTD_f_zstd1 && ZSTD_isLegacy(src, srcSize)) {
size_t decodedSize;
size_t const frameSize = ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
if (ZSTD_isError(frameSize)) return frameSize;
}
#endif
- if (srcSize >= 4) {
+ if (dctx->format == ZSTD_f_zstd1 && srcSize >= 4) {
U32 const magicNumber = MEM_readLE32(src);
DEBUGLOG(5, "reading magic number %08X", (unsigned)magicNumber);
if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
case ZSTDds_decodeSkippableHeader:
assert(src != NULL);
assert(srcSize <= ZSTD_SKIPPABLEHEADERSIZE);
+ assert(dctx->format != ZSTD_f_zstd1_magicless);
ZSTD_memcpy(dctx->headerBuffer + (ZSTD_SKIPPABLEHEADERSIZE - srcSize), src, srcSize); /* complete skippable header */
dctx->expected = MEM_readLE32(dctx->headerBuffer + ZSTD_FRAMEIDSIZE); /* note : dctx->expected can grow seriously large, beyond local buffer size */
dctx->stage = ZSTDds_skipFrame;
DEBUGLOG(4, "Consume header");
FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDDict(zds, ZSTD_getDDict(zds)), "");
- if ((MEM_readLE32(zds->headerBuffer) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */
+ if (zds->format == ZSTD_f_zstd1
+ && (MEM_readLE32(zds->headerBuffer) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */
zds->expected = MEM_readLE32(zds->headerBuffer + ZSTD_FRAMEIDSIZE);
zds->stage = ZSTDds_skipFrame;
} else {
--- /dev/null
+/*
+ * 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 decompression of magicless-format compressed data.
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "fuzz_helpers.h"
+#define ZSTD_STATIC_LINKING_ONLY
+#include "zstd.h"
+#include "fuzz_data_producer.h"
+
+static ZSTD_DCtx *dctx = NULL;
+
+int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
+{
+ // Give a random portion of src data to the producer, to use for parameter generation.
+ // The rest will be interpreted as magicless compressed data.
+ FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
+ size_t magiclessSize = FUZZ_dataProducer_reserveDataPrefix(producer);
+ const void* const magiclessSrc = src;
+ size_t const dstSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
+ void* const standardDst = FUZZ_malloc(dstSize);
+ void* const magiclessDst = FUZZ_malloc(dstSize);
+
+ // Create standard-format src from magicless-format src
+ const uint32_t zstd_magic = ZSTD_MAGICNUMBER;
+ size_t standardSize = sizeof(zstd_magic) + magiclessSize;
+ void* const standardSrc = FUZZ_malloc(standardSize);
+ memcpy(standardSrc, &zstd_magic, sizeof(zstd_magic)); // assume fuzzing on little-endian machine
+ memcpy(standardSrc + sizeof(zstd_magic), magiclessSrc, magiclessSize);
+
+ // Truncate to a single frame
+ {
+ const size_t standardFrameCompressedSize = ZSTD_findFrameCompressedSize(standardSrc, standardSize);
+ if (ZSTD_isError(standardFrameCompressedSize)) {
+ goto cleanup_and_return;
+ }
+ standardSize = standardFrameCompressedSize;
+ magiclessSize = standardFrameCompressedSize - sizeof(zstd_magic);
+ }
+
+ // Create DCtx if needed
+ if (!dctx) {
+ dctx = ZSTD_createDCtx();
+ FUZZ_ASSERT(dctx);
+ }
+
+ // Test one-shot decompression
+ {
+ FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
+ FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1));
+ const size_t standardRet = ZSTD_decompressDCtx(
+ dctx, standardDst, dstSize, standardSrc, standardSize);
+
+ FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
+ FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1_magicless));
+ const size_t magiclessRet = ZSTD_decompressDCtx(
+ dctx, magiclessDst, dstSize, magiclessSrc, magiclessSize);
+
+ // Standard accepts => magicless should accept
+ if (!ZSTD_isError(standardRet)) FUZZ_ZASSERT(magiclessRet);
+
+ // Magicless accepts => standard should accept
+ // NOTE: this is nice-to-have, please disable this check if it is difficult to satisfy.
+ if (!ZSTD_isError(magiclessRet)) FUZZ_ZASSERT(standardRet);
+
+ // If both accept, decompressed size and data should match
+ if (!ZSTD_isError(standardRet) && !ZSTD_isError(magiclessRet)) {
+ FUZZ_ASSERT(standardRet == magiclessRet);
+ if (standardRet > 0) {
+ FUZZ_ASSERT(
+ memcmp(standardDst, magiclessDst, standardRet) == 0
+ );
+ }
+ }
+ }
+
+ // Test streaming decompression
+ {
+ ZSTD_inBuffer standardIn = { standardSrc, standardSize, 0 };
+ ZSTD_inBuffer magiclessIn = { magiclessSrc, magiclessSize, 0 };
+ ZSTD_outBuffer standardOut = { standardDst, dstSize, 0 };
+ ZSTD_outBuffer magiclessOut = { magiclessDst, dstSize, 0 };
+
+ FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
+ FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1));
+ const size_t standardRet = ZSTD_decompressStream(dctx, &standardOut, &standardIn);
+
+ FUZZ_ZASSERT(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters));
+ FUZZ_ZASSERT(ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, ZSTD_f_zstd1_magicless));
+ const size_t magiclessRet = ZSTD_decompressStream(dctx, &magiclessOut, &magiclessIn);
+
+ // Standard accepts => magicless should accept
+ if (standardRet == 0) FUZZ_ASSERT(magiclessRet == 0);
+
+ // Magicless accepts => standard should accept
+ // NOTE: this is nice-to-have, please disable this check if it is difficult to satisfy.
+ if (magiclessRet == 0) FUZZ_ASSERT(standardRet == 0);
+
+ // If both accept, decompressed size and data should match
+ if (standardRet == 0 && magiclessRet == 0) {
+ FUZZ_ASSERT(standardOut.pos == magiclessOut.pos);
+ if (standardOut.pos > 0) {
+ FUZZ_ASSERT(
+ memcmp(standardOut.dst, magiclessOut.dst, standardOut.pos) == 0
+ );
+ }
+ }
+ }
+
+cleanup_and_return:
+#ifndef STATEFUL_FUZZING
+ ZSTD_freeDCtx(dctx); dctx = NULL;
+#endif
+ free(standardSrc);
+ free(standardDst);
+ free(magiclessDst);
+ FUZZ_dataProducer_free(producer);
+ return 0;
+}