Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/decompress/zstd_decompress.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,13 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx)
void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
{
size_t const toCopy = (size_t)((char*)(&dstDCtx->inBuff) - (char*)dstDCtx);
ZSTD_customMem const dstCustomMem = dstDCtx->customMem;
ZSTD_memcpy(dstDCtx, srcDCtx, toCopy); /* no need to copy workspace */
/* keep the destination's allocator: the destination context was
* allocated (and will be freed) with its own customMem.
* Copying the source's allocator instead would make ZSTD_freeDCtx()
* release a default-allocated context with mismatched callbacks. */
dstDCtx->customMem = dstCustomMem;
}

/* Given a dctx with a digested frame params, re-selects the correct ZSTD_DDict based on
Expand Down
36 changes: 36 additions & 0 deletions tests/fuzzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,22 @@ static U32 FUZ_highbit32(U32 v32)
/*=============================================
* Memory Tests
=============================================*/
/* allocator used by the ZSTD_copyDCtx() regression test:
* customFree flags when it is called on memory it must not own */
static void* FUZ_countingAlloc(void* counter, size_t size)
{
int* const flag = (int*)counter;
(void)flag;
return malloc(size);
}

static void FUZ_countingWrongFree(void* counter, void* address)
{
int* const flag = (int*)counter;
*flag += 1; /* this free() must never happen for the destination ctx */
free(address);
}

#if defined(__APPLE__) && defined(__MACH__)

#include <malloc/malloc.h> /* malloc_size */
Expand Down Expand Up @@ -1148,6 +1164,26 @@ static int basicUnitTests(U32 const seed, double compressibility)
}
DISPLAYLEVEL(3, "OK \n");

DISPLAYLEVEL(3, "test%3i : ZSTD_copyDCtx keeps the destination's allocator : ", testNb++);
{ /* ZSTD_copyDCtx() used to memcpy the source's customMem over the
* destination's one. When both contexts were created with different
* allocators, ZSTD_freeDCtx() would then release the destination
* context (and its resources) with the wrong callbacks.
* Here the mismatch is detected without faulting: the destination
* uses one that must never be called for the copy. */
static int wrongFreeCalled;
wrongFreeCalled = 0;
ZSTD_customMem const wrongMem = { FUZ_countingAlloc, FUZ_countingWrongFree, &wrongFreeCalled };
ZSTD_DCtx* const src = ZSTD_createDCtx_advanced(wrongMem);
ZSTD_DCtx* const dst = ZSTD_createDCtx(); /* default allocator */
assert(src != NULL); assert(dst != NULL);
ZSTD_copyDCtx(dst, src);
ZSTD_freeDCtx(dst); /* must use dst's own allocator */
if (wrongFreeCalled) goto _output_error; /* free(dst) went through src's callbacks */
ZSTD_freeDCtx(src); /* src's own free through src's callbacks is fine */
}
DISPLAYLEVEL(3, "OK \n");

DISPLAYLEVEL(3, "test%3i : misc unaccounted for zstd symbols : ", testNb++);
{
/* %p takes a void*. In ISO C, it's illegal to cast a function pointer
Expand Down