|  | // Copyright 2020 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <lib/syslog/cpp/log_settings.h> | 
|  | #include <lib/syslog/cpp/macros.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include <fbl/array.h> | 
|  | #include <src/lib/chunked-compression/chunked-archive.h> | 
|  | #include <src/lib/chunked-compression/chunked-decompressor.h> | 
|  | #include <src/lib/chunked-compression/test-utils.h> | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using chunked_compression::ChunkCountType; | 
|  | using chunked_compression::ChunkedDecompressor; | 
|  | using chunked_compression::HeaderReader; | 
|  | using chunked_compression::HeaderWriter; | 
|  | using chunked_compression::kChunkArchiveHeaderCrc32Offset; | 
|  | using chunked_compression::kChunkArchiveMinHeaderSize; | 
|  | using chunked_compression::kChunkArchiveNumChunksOffset; | 
|  | using chunked_compression::kStatusOk; | 
|  | using chunked_compression::SeekTable; | 
|  | using chunked_compression::SeekTableEntry; | 
|  | using chunked_compression::Status; | 
|  | using chunked_compression::test_utils::ComputeChecksum; | 
|  |  | 
|  | fbl::Array<uint8_t> CopyAndFixChecksum(const uint8_t *data, size_t size) { | 
|  | fbl::Array<uint8_t> data_copy(new uint8_t[size], size); | 
|  | memcpy(data_copy.get(), data, size); | 
|  |  | 
|  | static_assert(chunked_compression::kVersion == 2u, "Update this fuzzer if the format changes"); | 
|  | // Help guide the fuzzer by "fixing" the checksum. | 
|  | // Doing this requires knowing how large the archive ought to be, which is based on the | 
|  | // |num_chunks| field. | 
|  | ChunkCountType num_chunks = | 
|  | reinterpret_cast<ChunkCountType *>(data_copy.get() + kChunkArchiveNumChunksOffset)[0]; | 
|  |  | 
|  | size_t expected_header_size = HeaderWriter::MetadataSizeForNumFrames(num_chunks); | 
|  | uint32_t checksum; | 
|  | if (expected_header_size > size) { | 
|  | // Header is impossibly big, so don't try to compute the checksum. | 
|  | checksum = 0; | 
|  | } else { | 
|  | checksum = ComputeChecksum(data, expected_header_size); | 
|  | } | 
|  | reinterpret_cast<uint32_t *>(data_copy.get() + kChunkArchiveHeaderCrc32Offset)[0] = checksum; | 
|  |  | 
|  | return data_copy; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Fuzz test which attempts to decompress |data| as a chunked archive. | 
|  | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | 
|  | // Parse errors are logged at LOG_ERROR, so squelch them to avoid log spam. | 
|  | syslog::LogSettings settings; | 
|  | settings.min_log_level = syslog::LOG_FATAL; | 
|  | syslog::SetLogSettings(settings); | 
|  |  | 
|  | if (size < kChunkArchiveMinHeaderSize) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | fbl::Array<uint8_t> data_copy = CopyAndFixChecksum(data, size); | 
|  |  | 
|  | HeaderReader reader; | 
|  | SeekTable table; | 
|  | Status status = reader.Parse(data_copy.get(), size, size, &table); | 
|  | if (status != kStatusOk) { | 
|  | return 0; | 
|  | } | 
|  | size_t decompressed_size = table.DecompressedSize(); | 
|  | if (decompressed_size > 1024 * 1024) { | 
|  | // Disallow >1MB decompressions, since this will most likely just fail to allocate. | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | ChunkedDecompressor decompressor; | 
|  | fbl::Array<uint8_t> output(new uint8_t[decompressed_size], decompressed_size); | 
|  | for (const SeekTableEntry &entry : table.Entries()) { | 
|  | // These should have been checked during parsing. | 
|  | ZX_ASSERT(entry.compressed_offset + entry.compressed_size <= size); | 
|  | ZX_ASSERT(entry.decompressed_offset + entry.decompressed_size <= decompressed_size); | 
|  |  | 
|  | const uint8_t *input = data_copy.get() + entry.compressed_offset; | 
|  | size_t frame_sz = 0; | 
|  | decompressor.Decompress(table, input, entry.compressed_size, output.get(), output.size(), | 
|  | &frame_sz); | 
|  | ZX_ASSERT(frame_sz <= decompressed_size); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |