mirror of
https://github.com/facebook/zstd.git
synced 2025-11-24 12:01:06 +03:00
fixing potential over-reads
detected by @terrelln, these issue could be triggered in specific scenarios namely decompression of certain invalid magic-less frames, or requested properties from certain invalid skippable frames.
This commit is contained in:
@@ -589,49 +589,52 @@ static size_t readSkippableFrameSize(void const* src, size_t srcSize)
|
|||||||
sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE);
|
sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE);
|
||||||
RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32,
|
RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32,
|
||||||
frameParameter_unsupported, "");
|
frameParameter_unsupported, "");
|
||||||
{
|
{ size_t const skippableSize = skippableHeaderSize + sizeU32;
|
||||||
size_t const skippableSize = skippableHeaderSize + sizeU32;
|
|
||||||
RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong, "");
|
RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong, "");
|
||||||
return skippableSize;
|
return skippableSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! ZSTD_readSkippableFrame() :
|
/*! ZSTD_readSkippableFrame() :
|
||||||
* Retrieves a zstd skippable frame containing data given by src, and writes it to dst buffer.
|
* Retrieves content of a skippable frame, and writes it to dst buffer.
|
||||||
*
|
*
|
||||||
* The parameter magicVariant will receive the magicVariant that was supplied when the frame was written,
|
* The parameter magicVariant will receive the magicVariant that was supplied when the frame was written,
|
||||||
* i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START. This can be NULL if the caller is not interested
|
* i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START. This can be NULL if the caller is not interested
|
||||||
* in the magicVariant.
|
* in the magicVariant.
|
||||||
*
|
*
|
||||||
* Returns an error if destination buffer is not large enough, or if the frame is not skippable.
|
* Returns an error if destination buffer is not large enough, or if this is not a valid skippable frame.
|
||||||
*
|
*
|
||||||
* @return : number of bytes written or a ZSTD error.
|
* @return : number of bytes written or a ZSTD error.
|
||||||
*/
|
*/
|
||||||
ZSTDLIB_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, unsigned* magicVariant,
|
size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity,
|
||||||
const void* src, size_t srcSize)
|
unsigned* magicVariant, /* optional, can be NULL */
|
||||||
|
const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
U32 const magicNumber = MEM_readLE32(src);
|
RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong, "");
|
||||||
size_t skippableFrameSize = readSkippableFrameSize(src, srcSize);
|
|
||||||
size_t skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE;
|
|
||||||
|
|
||||||
/* check input validity */
|
{ U32 const magicNumber = MEM_readLE32(src);
|
||||||
RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, "");
|
size_t skippableFrameSize = readSkippableFrameSize(src, srcSize);
|
||||||
RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, "");
|
size_t skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE;
|
||||||
RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, "");
|
|
||||||
|
|
||||||
/* deliver payload */
|
/* check input validity */
|
||||||
if (skippableContentSize > 0 && dst != NULL)
|
RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, "");
|
||||||
ZSTD_memcpy(dst, (const BYTE *)src + ZSTD_SKIPPABLEHEADERSIZE, skippableContentSize);
|
RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, "");
|
||||||
if (magicVariant != NULL)
|
RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, "");
|
||||||
*magicVariant = magicNumber - ZSTD_MAGIC_SKIPPABLE_START;
|
|
||||||
return skippableContentSize;
|
/* deliver payload */
|
||||||
|
if (skippableContentSize > 0 && dst != NULL)
|
||||||
|
ZSTD_memcpy(dst, (const BYTE *)src + ZSTD_SKIPPABLEHEADERSIZE, skippableContentSize);
|
||||||
|
if (magicVariant != NULL)
|
||||||
|
*magicVariant = magicNumber - ZSTD_MAGIC_SKIPPABLE_START;
|
||||||
|
return skippableContentSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ZSTD_findDecompressedSize() :
|
/** ZSTD_findDecompressedSize() :
|
||||||
* compatible with legacy mode
|
|
||||||
* `srcSize` must be the exact length of some number of ZSTD compressed and/or
|
* `srcSize` must be the exact length of some number of ZSTD compressed and/or
|
||||||
* skippable frames
|
* skippable frames
|
||||||
* @return : decompressed size of the frames contained */
|
* note: compatible with legacy mode
|
||||||
|
* @return : decompressed size of the frames contained */
|
||||||
unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
|
unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
|
||||||
{
|
{
|
||||||
unsigned long long totalDstSize = 0;
|
unsigned long long totalDstSize = 0;
|
||||||
@@ -641,9 +644,7 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
|
|||||||
|
|
||||||
if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
|
if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
|
||||||
size_t const skippableSize = readSkippableFrameSize(src, srcSize);
|
size_t const skippableSize = readSkippableFrameSize(src, srcSize);
|
||||||
if (ZSTD_isError(skippableSize)) {
|
if (ZSTD_isError(skippableSize)) return ZSTD_CONTENTSIZE_ERROR;
|
||||||
return ZSTD_CONTENTSIZE_ERROR;
|
|
||||||
}
|
|
||||||
assert(skippableSize <= srcSize);
|
assert(skippableSize <= srcSize);
|
||||||
|
|
||||||
src = (const BYTE *)src + skippableSize;
|
src = (const BYTE *)src + skippableSize;
|
||||||
@@ -651,17 +652,17 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
{ unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize);
|
{ unsigned long long const fcs = ZSTD_getFrameContentSize(src, srcSize);
|
||||||
if (ret >= ZSTD_CONTENTSIZE_ERROR) return ret;
|
if (fcs >= ZSTD_CONTENTSIZE_ERROR) return fcs;
|
||||||
|
|
||||||
/* check for overflow */
|
if (totalDstSize + fcs < totalDstSize)
|
||||||
if (totalDstSize + ret < totalDstSize) return ZSTD_CONTENTSIZE_ERROR;
|
return ZSTD_CONTENTSIZE_ERROR; /* check for overflow */
|
||||||
totalDstSize += ret;
|
totalDstSize += fcs;
|
||||||
}
|
}
|
||||||
|
/* skip to next frame */
|
||||||
{ size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize);
|
{ size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize);
|
||||||
if (ZSTD_isError(frameSrcSize)) {
|
if (ZSTD_isError(frameSrcSize)) return ZSTD_CONTENTSIZE_ERROR;
|
||||||
return ZSTD_CONTENTSIZE_ERROR;
|
assert(frameSrcSize <= srcSize);
|
||||||
}
|
|
||||||
|
|
||||||
src = (const BYTE *)src + frameSrcSize;
|
src = (const BYTE *)src + frameSrcSize;
|
||||||
srcSize -= frameSrcSize;
|
srcSize -= frameSrcSize;
|
||||||
@@ -1091,17 +1092,18 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{ U32 const magicNumber = MEM_readLE32(src);
|
if (srcSize >= 4) {
|
||||||
DEBUGLOG(4, "reading magic number %08X (expecting %08X)",
|
U32 const magicNumber = MEM_readLE32(src);
|
||||||
(unsigned)magicNumber, ZSTD_MAGICNUMBER);
|
DEBUGLOG(5, "reading magic number %08X", (unsigned)magicNumber);
|
||||||
if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
|
if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
|
||||||
|
/* skippable frame detected : skip it */
|
||||||
size_t const skippableSize = readSkippableFrameSize(src, srcSize);
|
size_t const skippableSize = readSkippableFrameSize(src, srcSize);
|
||||||
FORWARD_IF_ERROR(skippableSize, "readSkippableFrameSize failed");
|
FORWARD_IF_ERROR(skippableSize, "invalid skippable frame");
|
||||||
assert(skippableSize <= srcSize);
|
assert(skippableSize <= srcSize);
|
||||||
|
|
||||||
src = (const BYTE *)src + skippableSize;
|
src = (const BYTE *)src + skippableSize;
|
||||||
srcSize -= skippableSize;
|
srcSize -= skippableSize;
|
||||||
continue;
|
continue; /* check next frame */
|
||||||
} }
|
} }
|
||||||
|
|
||||||
if (ddict) {
|
if (ddict) {
|
||||||
|
|||||||
Reference in New Issue
Block a user