Add support for in-place decompression
* Add a function and macro ZSTD_decompressionMargin() that computes the decompression margin for in-place decompression. The function computes a tight margin that works in all cases, and the macro computes an upper bound that will only work if flush isn't used. * When doing in-place decompression, make sure that our output buffer doesn't overlap with the input buffer. This ensures that we don't decide to use the portion of the output buffer that overlaps the input buffer for temporary memory, like for literals. * Add a simple unit test. * Add in-place decompression to the simple_round_trip and stream_round_trip fuzzers. This should help verify that our margin stays correct.
This commit is contained in:
committed by
Nick Terrell
parent
423500d1ae
commit
5b266196a4
@@ -26,6 +26,23 @@
|
||||
static ZSTD_CCtx *cctx = NULL;
|
||||
static ZSTD_DCtx *dctx = NULL;
|
||||
|
||||
static size_t getDecompressionMargin(void const* compressed, size_t cSize, size_t srcSize, int hasSmallBlocks)
|
||||
{
|
||||
size_t margin = ZSTD_decompressionMargin(compressed, cSize);
|
||||
if (!hasSmallBlocks) {
|
||||
/* The macro should be correct in this case, but it may be smaller
|
||||
* because of e.g. block splitting, so take the smaller of the two.
|
||||
*/
|
||||
ZSTD_frameHeader zfh;
|
||||
size_t marginM;
|
||||
FUZZ_ZASSERT(ZSTD_getFrameHeader(&zfh, compressed, cSize));
|
||||
marginM = ZSTD_DECOMPRESSION_MARGIN(srcSize, zfh.blockSizeMax);
|
||||
if (marginM < margin)
|
||||
margin = marginM;
|
||||
}
|
||||
return margin;
|
||||
}
|
||||
|
||||
static size_t roundTripTest(void *result, size_t resultCapacity,
|
||||
void *compressed, size_t compressedCapacity,
|
||||
const void *src, size_t srcSize,
|
||||
@@ -67,6 +84,25 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
|
||||
}
|
||||
dSize = ZSTD_decompressDCtx(dctx, result, resultCapacity, compressed, cSize);
|
||||
FUZZ_ZASSERT(dSize);
|
||||
FUZZ_ASSERT_MSG(dSize == srcSize, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, result, dSize), "Corruption!");
|
||||
|
||||
{
|
||||
size_t margin = getDecompressionMargin(compressed, cSize, srcSize, targetCBlockSize);
|
||||
size_t const outputSize = srcSize + margin;
|
||||
char* const output = (char*)FUZZ_malloc(outputSize);
|
||||
char* const input = output + outputSize - cSize;
|
||||
FUZZ_ASSERT(outputSize >= cSize);
|
||||
memcpy(input, compressed, cSize);
|
||||
|
||||
dSize = ZSTD_decompressDCtx(dctx, output, outputSize, input, cSize);
|
||||
FUZZ_ZASSERT(dSize);
|
||||
FUZZ_ASSERT_MSG(dSize == srcSize, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, output, srcSize), "Corruption!");
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
/* When superblock is enabled make sure we don't expand the block more than expected.
|
||||
* NOTE: This test is currently disabled because superblock mode can arbitrarily
|
||||
* expand the block in the worst case. Once superblock mode has been improved we can
|
||||
@@ -120,13 +156,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
FUZZ_ASSERT(dctx);
|
||||
}
|
||||
|
||||
{
|
||||
size_t const result =
|
||||
roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
|
||||
FUZZ_ZASSERT(result);
|
||||
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
|
||||
}
|
||||
roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
|
||||
free(rBuf);
|
||||
free(cBuf);
|
||||
FUZZ_dataProducer_free(producer);
|
||||
|
||||
@@ -166,6 +166,24 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
FUZZ_ZASSERT(rSize);
|
||||
FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
|
||||
|
||||
/* Test in-place decompression (note the macro doesn't work in this case) */
|
||||
{
|
||||
size_t const margin = ZSTD_decompressionMargin(cBuf, cSize);
|
||||
size_t const outputSize = size + margin;
|
||||
char* const output = (char*)FUZZ_malloc(outputSize);
|
||||
char* const input = output + outputSize - cSize;
|
||||
size_t dSize;
|
||||
FUZZ_ASSERT(outputSize >= cSize);
|
||||
memcpy(input, cBuf, cSize);
|
||||
|
||||
dSize = ZSTD_decompressDCtx(dctx, output, outputSize, input, cSize);
|
||||
FUZZ_ZASSERT(dSize);
|
||||
FUZZ_ASSERT_MSG(dSize == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, output, size), "Corruption!");
|
||||
|
||||
free(output);
|
||||
}
|
||||
}
|
||||
|
||||
FUZZ_dataProducer_free(producer);
|
||||
|
||||
Reference in New Issue
Block a user