Harden LZ4_Decompress.

Add fuzzer for LZ4_Decompress.
This commit is contained in:
Syoyo Fujita
2022-09-22 03:56:17 +09:00
parent 9a2618811a
commit 5f489061fe
3 changed files with 107 additions and 2 deletions

View File

@@ -137,6 +137,20 @@ size_t LZ4Compression::DecompressFromBuffer(char const *compressed,
// Check first byte for # chunks.
int nChunks = *compressed++;
if (nChunks > 127) {
if (err) {
(*err) =
"Too many chunks in LZ4 compressed data.\n";
}
return 0;
}
//std::cout << "compressedSize = " << compressedSize << "\n";
//std::cout << "maxOutputSize = " << maxOutputSize << "\n";
//std::cout << "nChunks = " << nChunks << "\n";
size_t consumedCompressedSize = 1;
if (nChunks == 0) {
// Just one.
int nDecompressed = LZ4_decompress_safe(compressed, output,
@@ -154,14 +168,37 @@ size_t LZ4Compression::DecompressFromBuffer(char const *compressed,
} else {
// Do each chunk.
size_t totalDecompressed = 0;
for (int i = 0; i != nChunks; ++i) {
for (int i = 0; i < nChunks; ++i) {
int32_t chunkSize = 0;
memcpy(&chunkSize, compressed, sizeof(chunkSize));
if (chunkSize > LZ4_MAX_INPUT_SIZE) {
if (err) {
(*err) += "ChunkSize exceeds LZ4_MAX_INPUT_SIZE.\n";
}
return 0;
}
if (chunkSize <= 0) {
if (err) {
(*err) += "Invalid ChunkSize.\n";
}
return 0;
}
//std::cout << "chunkSize = " << chunkSize << "\n";
consumedCompressedSize += sizeof(chunkSize);
//std::cout << "consumedCompressedSize = " << consumedCompressedSize << "\n";
//std::cout << "compressedSize = " << compressedSize << "\n";
if (consumedCompressedSize > compressedSize) {
if (err) {
(*err) += "Total chunk size exceeds input compressedSize.\n";
}
return 0;
}
compressed += sizeof(chunkSize);
int nDecompressed = LZ4_decompress_safe(
compressed, output, chunkSize,
int(std::min<size_t>(LZ4_MAX_INPUT_SIZE, maxOutputSize)));
if (nDecompressed < 0) {
if (nDecompressed <= 0) {
if (err) {
(*err) =
"Failed to decompress data, possibly corrupt? "
@@ -170,6 +207,14 @@ size_t LZ4Compression::DecompressFromBuffer(char const *compressed,
}
return 0;
}
//std::cout << "nDecompressed = " << nDecompressed << "\n";
if (nDecompressed > maxOutputSize) {
if (err) {
(*err) =
"Failed to decompress data, possibly corrupt?\n";
}
return 0;
}
compressed += chunkSize;
output += nDecompressed;
maxOutputSize -= size_t(nDecompressed);

View File

@@ -0,0 +1,51 @@
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include <iostream>
#include "lz4-compression.hh"
static int lz4_decompress_main(const uint8_t *data, size_t size)
{
if (size < (16+1)) return -1;
// first 8 byte: uncompress size
// second 8 byte: compress size
// 1 byte: # chunks
// remaining : compressed data
uint64_t uncompressedSize; // nInts
uint64_t compressedSize;
memcpy(&uncompressedSize, data, 8);
memcpy(&compressedSize, data+8, 8);
// FIXME: Currently up to 4GB
if ((uncompressedSize < 4) || (uncompressedSize > 1024*1024*4)) {
return 0;
}
if ((compressedSize < 4) || (compressedSize > 1024*1024*4)) {
return 0;
}
if (compressedSize > (size - (16+1))) {
return 0;
}
std::vector<char> dst;
dst.resize(uncompressedSize);
std::string err;
size_t n = tinyusdz::LZ4Compression::DecompressFromBuffer(reinterpret_cast<const char *>(data + 8), dst.data(), compressedSize, uncompressedSize, &err);
(void)n;
return 0;
}
extern "C"
int LLVMFuzzerTestOneInput(std::uint8_t const* data, std::size_t size)
{
int ret = lz4_decompress_main(data, size);
return ret;
}

View File

@@ -69,6 +69,15 @@ executable('fuzz_intcoding',
cpp_args : '-fsanitize=address,fuzzer',
link_args : '-fsanitize=address,fuzzer' )
executable('fuzz_lz4_decompress',
'../../src/lz4/lz4.c',
'lz4_decompress_fuzzmain.cc',
'../../src/lz4-compression.cc',
install: true,
include_directories : incdirs,
cpp_args : '-fsanitize=address,fuzzer',
link_args : '-fsanitize=address,fuzzer' )
executable('fuzz_usdaparser',
'usdaparser_fuzzmain.cc',