[lib] Fix NULL pointer dereference
When the output buffer is `NULL` with size 0, but the frame content size is non-zero, we will write to the NULL pointer because our bounds check underflowed. This was exposed by a recent PR that allowed an empty frame into the single-pass shortcut in streaming mode. * Fix the bug. * Fix another NULL dereference in zstd-v1. * Overflow checks in 32-bit mode. * Add a dedicated test. * Expose the bug in the dedicated simple_decompress fuzzer. * Switch all mallocs in fuzzers to return NULL for size=0. * Fix a new timeout in a fuzzer. Neither clang nor gcc show a decompression speed regression on x86-64. On x86-32 clang is slightly positive and gcc loses 2.5% of speed. Credit to OSS-Fuzz.
This commit is contained in:
committed by
Nick Terrell
parent
ad8dbae1b7
commit
5717bd39ee
+1
-1
@@ -42,7 +42,7 @@ FUZZ_ARFLAGS := $(ARFLAGS)
|
||||
FUZZ_TARGET_FLAGS = $(FUZZ_CPPFLAGS) $(FUZZ_CXXFLAGS) $(FUZZ_LDFLAGS)
|
||||
|
||||
FUZZ_HEADERS := fuzz_helpers.h fuzz.h zstd_helpers.h fuzz_data_producer.h
|
||||
FUZZ_SRC := $(PRGDIR)/util.c zstd_helpers.c fuzz_data_producer.c
|
||||
FUZZ_SRC := $(PRGDIR)/util.c fuzz_helpers.c zstd_helpers.c fuzz_data_producer.c
|
||||
|
||||
ZSTDCOMMON_SRC := $(ZSTDDIR)/common/*.c
|
||||
ZSTDCOMP_SRC := $(ZSTDDIR)/compress/*.c
|
||||
|
||||
@@ -32,9 +32,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (neededBufSize > bufSize) {
|
||||
free(rBuf);
|
||||
rBuf = malloc(neededBufSize);
|
||||
rBuf = FUZZ_malloc(neededBufSize);
|
||||
bufSize = neededBufSize;
|
||||
FUZZ_ASSERT(rBuf);
|
||||
}
|
||||
if (!dctx) {
|
||||
dctx = ZSTD_createDCtx();
|
||||
|
||||
@@ -43,7 +43,9 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
|
||||
FUZZ_ZASSERT(ret);
|
||||
if (ret == 0) {
|
||||
FUZZ_ASSERT(resultCapacity >= srcSize);
|
||||
memcpy(result, src, srcSize);
|
||||
if (srcSize > 0) {
|
||||
memcpy(result, src, srcSize);
|
||||
}
|
||||
return srcSize;
|
||||
}
|
||||
ZSTD_decompressBegin(dctx);
|
||||
@@ -67,10 +69,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
if (neededBufSize > bufSize || !cBuf || !rBuf) {
|
||||
free(cBuf);
|
||||
free(rBuf);
|
||||
cBuf = malloc(neededBufSize);
|
||||
rBuf = malloc(neededBufSize);
|
||||
cBuf = FUZZ_malloc(neededBufSize);
|
||||
rBuf = FUZZ_malloc(neededBufSize);
|
||||
bufSize = neededBufSize;
|
||||
FUZZ_ASSERT(cBuf && rBuf);
|
||||
}
|
||||
if (!cctx) {
|
||||
cctx = ZSTD_createCCtx();
|
||||
@@ -87,7 +88,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
cLevel);
|
||||
FUZZ_ZASSERT(result);
|
||||
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
|
||||
}
|
||||
FUZZ_dataProducer_free(producer);
|
||||
#ifndef STATEFUL_FUZZING
|
||||
|
||||
@@ -55,8 +55,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
|
||||
{
|
||||
size_t const bufSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
|
||||
void* rBuf = malloc(bufSize);
|
||||
FUZZ_ASSERT(rBuf);
|
||||
void* rBuf = FUZZ_malloc(bufSize);
|
||||
if (ddict) {
|
||||
ZSTD_decompress_usingDDict(dctx, rBuf, bufSize, src, size, ddict);
|
||||
} else {
|
||||
|
||||
@@ -79,11 +79,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
DEBUGLOG(2, "Dict content type %d", dct);
|
||||
DEBUGLOG(2, "Dict size %u", (unsigned)size);
|
||||
|
||||
void* const rBuf = malloc(size);
|
||||
FUZZ_ASSERT(rBuf);
|
||||
void* const rBuf = FUZZ_malloc(size);
|
||||
size_t const cBufSize = ZSTD_compressBound(size);
|
||||
void* const cBuf = malloc(cBufSize);
|
||||
FUZZ_ASSERT(cBuf);
|
||||
void* const cBuf = FUZZ_malloc(cBufSize);
|
||||
|
||||
size_t const cSize =
|
||||
compress(cBuf, cBufSize, src, size, src, size, dlm, dct, refPrefix);
|
||||
@@ -95,7 +93,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
size_t const rSize =
|
||||
decompress(rBuf, size, cBuf, cSize, src, size, dlm, dct, refPrefix);
|
||||
FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
|
||||
|
||||
out:
|
||||
free(cBuf);
|
||||
|
||||
@@ -84,7 +84,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
size = FUZZ_dataProducer_reserveDataPrefix(producer);
|
||||
|
||||
size_t const rBufSize = size;
|
||||
void* rBuf = malloc(rBufSize);
|
||||
void* rBuf = FUZZ_malloc(rBufSize);
|
||||
size_t cBufSize = ZSTD_compressBound(size) * 2;
|
||||
void *cBuf;
|
||||
/* Half of the time fuzz with a 1 byte smaller output size.
|
||||
@@ -92,7 +92,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
* giving us 4 bytes of overhead.
|
||||
*/
|
||||
cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
|
||||
cBuf = malloc(cBufSize);
|
||||
cBuf = FUZZ_malloc(cBufSize);
|
||||
|
||||
if (!cctx) {
|
||||
cctx = ZSTD_createCCtx();
|
||||
@@ -108,7 +108,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
|
||||
FUZZ_ZASSERT(result);
|
||||
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
|
||||
}
|
||||
free(rBuf);
|
||||
free(cBuf);
|
||||
|
||||
@@ -16,9 +16,7 @@ struct FUZZ_dataProducer_s{
|
||||
};
|
||||
|
||||
FUZZ_dataProducer_t *FUZZ_dataProducer_create(const uint8_t *data, size_t size) {
|
||||
FUZZ_dataProducer_t *producer = malloc(sizeof(FUZZ_dataProducer_t));
|
||||
|
||||
FUZZ_ASSERT(producer != NULL);
|
||||
FUZZ_dataProducer_t *producer = FUZZ_malloc(sizeof(FUZZ_dataProducer_t));
|
||||
|
||||
producer->data = data;
|
||||
producer->size = size;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Facebook, Inc.
|
||||
* 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.
|
||||
*/
|
||||
#include "fuzz_helpers.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void* FUZZ_malloc(size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
void* const mem = malloc(size);
|
||||
FUZZ_ASSERT(mem);
|
||||
return mem;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int FUZZ_memcmp(void const* lhs, void const* rhs, size_t size)
|
||||
{
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
return memcmp(lhs, rhs, size);
|
||||
}
|
||||
@@ -56,6 +56,17 @@ extern "C" {
|
||||
#define FUZZ_STATIC static
|
||||
#endif
|
||||
|
||||
/**
|
||||
* malloc except return NULL for zero sized data and FUZZ_ASSERT
|
||||
* that malloc doesn't fail.
|
||||
*/
|
||||
void* FUZZ_malloc(size_t size);
|
||||
|
||||
/**
|
||||
* memcmp but accepts NULL.
|
||||
*/
|
||||
int FUZZ_memcmp(void const* lhs, void const* rhs, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -41,8 +41,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
FUZZ_ASSERT(cctx);
|
||||
}
|
||||
|
||||
void *rBuf = malloc(bufSize);
|
||||
FUZZ_ASSERT(rBuf);
|
||||
void *rBuf = FUZZ_malloc(bufSize);
|
||||
ZSTD_compressCCtx(cctx, rBuf, bufSize, src, size, cLevel);
|
||||
free(rBuf);
|
||||
FUZZ_dataProducer_free(producer);
|
||||
|
||||
@@ -35,8 +35,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
}
|
||||
|
||||
size_t const bufSize = FUZZ_dataProducer_uint32Range(producer, 0, 10 * size);
|
||||
void *rBuf = malloc(bufSize);
|
||||
FUZZ_ASSERT(rBuf);
|
||||
void *rBuf = FUZZ_malloc(bufSize);
|
||||
|
||||
ZSTD_decompressDCtx(dctx, rBuf, bufSize, src, size);
|
||||
free(rBuf);
|
||||
|
||||
@@ -70,7 +70,7 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
{
|
||||
size_t const rBufSize = size;
|
||||
void* rBuf = malloc(rBufSize);
|
||||
void* rBuf = FUZZ_malloc(rBufSize);
|
||||
size_t cBufSize = ZSTD_compressBound(size);
|
||||
void* cBuf;
|
||||
|
||||
@@ -85,9 +85,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
*/
|
||||
cBufSize -= FUZZ_dataProducer_uint32Range(producer, 0, 1);
|
||||
|
||||
cBuf = malloc(cBufSize);
|
||||
|
||||
FUZZ_ASSERT(cBuf && rBuf);
|
||||
cBuf = FUZZ_malloc(cBufSize);
|
||||
|
||||
if (!cctx) {
|
||||
cctx = ZSTD_createCCtx();
|
||||
@@ -103,7 +101,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
roundTripTest(rBuf, rBufSize, cBuf, cBufSize, src, size, producer);
|
||||
FUZZ_ZASSERT(result);
|
||||
FUZZ_ASSERT_MSG(result == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
|
||||
}
|
||||
free(rBuf);
|
||||
free(cBuf);
|
||||
|
||||
@@ -76,9 +76,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
|
||||
/* Allocate all buffers and contexts if not already allocated */
|
||||
if (!buf) {
|
||||
buf = malloc(kBufSize);
|
||||
FUZZ_ASSERT(buf);
|
||||
}
|
||||
buf = FUZZ_malloc(kBufSize);
|
||||
}
|
||||
|
||||
if (!dstream) {
|
||||
dstream = ZSTD_createDStream();
|
||||
@@ -99,7 +98,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
ZSTD_inBuffer in = makeInBuffer(&src, &size, producer, prevInWasZero ? 1 : 0);
|
||||
prevInWasZero = in.size == 0;
|
||||
while (in.pos != in.size) {
|
||||
if (!stableOutBuffer || FUZZ_dataProducer_uint32Range(producer, 0, 100) == 55) {
|
||||
if (!stableOutBuffer || prevOutWasZero || FUZZ_dataProducer_uint32Range(producer, 0, 100) == 55) {
|
||||
out = makeOutBuffer(producer, prevOutWasZero ? 1 : 0);
|
||||
}
|
||||
prevOutWasZero = out.size == 0;
|
||||
|
||||
@@ -146,10 +146,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
if (neededBufSize > bufSize) {
|
||||
free(cBuf);
|
||||
free(rBuf);
|
||||
cBuf = (uint8_t*)malloc(neededBufSize);
|
||||
rBuf = (uint8_t*)malloc(neededBufSize);
|
||||
cBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
|
||||
rBuf = (uint8_t*)FUZZ_malloc(neededBufSize);
|
||||
bufSize = neededBufSize;
|
||||
FUZZ_ASSERT(cBuf && rBuf);
|
||||
}
|
||||
if (!cctx) {
|
||||
cctx = ZSTD_createCCtx();
|
||||
@@ -166,7 +165,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
ZSTD_decompressDCtx(dctx, rBuf, neededBufSize, cBuf, cSize);
|
||||
FUZZ_ZASSERT(rSize);
|
||||
FUZZ_ASSERT_MSG(rSize == size, "Incorrect regenerated size");
|
||||
FUZZ_ASSERT_MSG(!memcmp(src, rBuf, size), "Corruption!");
|
||||
FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, rBuf, size), "Corruption!");
|
||||
}
|
||||
|
||||
FUZZ_dataProducer_free(producer);
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
|
||||
{
|
||||
ZSTD_frameHeader zfh;
|
||||
if (size == 0) {
|
||||
src = NULL;
|
||||
}
|
||||
/* You can fuzz any helper functions here that are fast, and take zstd
|
||||
* compressed data as input. E.g. don't expect the input to be a dictionary,
|
||||
* so don't fuzz ZSTD_getDictID_fromDict().
|
||||
|
||||
@@ -106,14 +106,13 @@ FUZZ_dict_t FUZZ_train(void const* src, size_t srcSize, FUZZ_dataProducer_t *pro
|
||||
{
|
||||
size_t const dictSize = MAX(srcSize / 8, 1024);
|
||||
size_t const totalSampleSize = dictSize * 11;
|
||||
FUZZ_dict_t dict = { malloc(dictSize), dictSize };
|
||||
char* const samples = (char*)malloc(totalSampleSize);
|
||||
FUZZ_dict_t dict = { FUZZ_malloc(dictSize), dictSize };
|
||||
char* const samples = (char*)FUZZ_malloc(totalSampleSize);
|
||||
unsigned nbSamples = 100;
|
||||
size_t* const samplesSizes = (size_t*)malloc(sizeof(size_t) * nbSamples);
|
||||
size_t* const samplesSizes = (size_t*)FUZZ_malloc(sizeof(size_t) * nbSamples);
|
||||
size_t pos = 0;
|
||||
size_t sample = 0;
|
||||
ZDICT_fastCover_params_t params;
|
||||
FUZZ_ASSERT(dict.buff && samples && samplesSizes);
|
||||
|
||||
for (sample = 0; sample < nbSamples; ++sample) {
|
||||
size_t const remaining = totalSampleSize - pos;
|
||||
@@ -123,7 +122,6 @@ FUZZ_dict_t FUZZ_train(void const* src, size_t srcSize, FUZZ_dataProducer_t *pro
|
||||
memcpy(samples + pos, src + offset, toCopy);
|
||||
pos += toCopy;
|
||||
samplesSizes[sample] = toCopy;
|
||||
|
||||
}
|
||||
memset(samples + pos, 0, totalSampleSize - pos);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user