diff options
author | Hans Wennborg <hans@chromium.org> | 2020-08-04 11:38:55 +0000 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-08-04 11:38:55 +0000 |
commit | 493d429924861e336cfae702403a0c4145c173ae (patch) | |
tree | a41a3a5167bf039d0d73c1eaaa6acda9594dba71 /contrib | |
parent | a21a4e8f27567b7c36f8274bf16ebca78b9a68ab (diff) | |
download | zlib-493d429924861e336cfae702403a0c4145c173ae.tar.gz |
[zlib] Add streaming inflate fuzzer
This adds a new fuzzer which first deflates and then inflates some data
one chunk at a time and verifies that it round-trips correctly. It's
targeted at the recent bug where inffast_chunk was called with
overlapping from and out buffers.
I've verified that the fuzzer detects the incorrect inflate results when
using Clang's -basic-aa-recphi option before the bug fix in e0f88a9.
Furthermore, this patch adds asserts in inffast_chunk and other
functions that verify there's no overlap of restrict-qualified buffers.
(I've checked that the fuzzer hits the inffast_chunk assert before
e0f88a9).
To make those asserts work, this changes the build config to define
ZLIB_DEBUG in debug and fuzzer builds.
Bug: 1103818, 708726
Change-Id: Ia3d5e7906b8b1a65d56a589d8a81d17661e638c2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332705
Commit-Queue: Hans Wennborg <hans@chromium.org>
Reviewed-by: Nico Weber <thakis@chromium.org>
Reviewed-by: Adenilson Cavalcanti <cavalcantii@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#794482}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 3eda17e22e6a3c4e44fdfa778b7d55f0a7c82e75
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/optimizations/chunkcopy.h | 13 | ||||
-rw-r--r-- | contrib/tests/fuzzers/BUILD.gn | 6 | ||||
-rw-r--r-- | contrib/tests/fuzzers/streaming_inflate_fuzzer.cc | 74 |
3 files changed, 93 insertions, 0 deletions
diff --git a/contrib/optimizations/chunkcopy.h b/contrib/optimizations/chunkcopy.h index 7cbcf82..25d4fe1 100644 --- a/contrib/optimizations/chunkcopy.h +++ b/contrib/optimizations/chunkcopy.h @@ -112,6 +112,10 @@ static inline unsigned char FAR* chunkcopy_core_safe( Assert(out + len <= limit, "chunk copy exceeds safety limit"); if ((limit - out) < (ptrdiff_t)CHUNKCOPY_CHUNK_SIZE) { const unsigned char FAR* Z_RESTRICT rfrom = from; + Assert((uintptr_t)out - (uintptr_t)from >= len, + "invalid restrict in chunkcopy_core_safe"); + Assert((uintptr_t)from - (uintptr_t)out >= len, + "invalid restrict in chunkcopy_core_safe"); if (len & 8) { Z_BUILTIN_MEMCPY(out, rfrom, 8); out += 8; @@ -338,6 +342,10 @@ static inline unsigned char FAR* chunkcopy_relaxed( unsigned char FAR* Z_RESTRICT out, const unsigned char FAR* Z_RESTRICT from, unsigned len) { + Assert((uintptr_t)out - (uintptr_t)from >= len, + "invalid restrict in chunkcopy_relaxed"); + Assert((uintptr_t)from - (uintptr_t)out >= len, + "invalid restrict in chunkcopy_relaxed"); return chunkcopy_core(out, from, len); } @@ -360,6 +368,11 @@ static inline unsigned char FAR* chunkcopy_safe( unsigned len, unsigned char FAR* limit) { Assert(out + len <= limit, "chunk copy exceeds safety limit"); + Assert((uintptr_t)out - (uintptr_t)from >= len, + "invalid restrict in chunkcopy_safe"); + Assert((uintptr_t)from - (uintptr_t)out >= len, + "invalid restrict in chunkcopy_safe"); + return chunkcopy_core_safe(out, from, len, limit); } diff --git a/contrib/tests/fuzzers/BUILD.gn b/contrib/tests/fuzzers/BUILD.gn index 34c3b43..10abe00 100644 --- a/contrib/tests/fuzzers/BUILD.gn +++ b/contrib/tests/fuzzers/BUILD.gn @@ -18,6 +18,12 @@ fuzzer_test("zlib_inflate_fuzzer") { deps = [ "../../../:zlib" ] } +fuzzer_test("zlib_streaming_inflate_fuzzer") { + sources = [ "streaming_inflate_fuzzer.cc" ] + deps = [ "../../../:zlib" ] + libfuzzer_options = [ "max_len=256000" ] +} + fuzzer_test("zlib_deflate_set_dictionary_fuzzer") { sources = [ "deflate_set_dictionary_fuzzer.cc" ] deps = [ "../../../:zlib" ] diff --git a/contrib/tests/fuzzers/streaming_inflate_fuzzer.cc b/contrib/tests/fuzzers/streaming_inflate_fuzzer.cc new file mode 100644 index 0000000..de4d216 --- /dev/null +++ b/contrib/tests/fuzzers/streaming_inflate_fuzzer.cc @@ -0,0 +1,74 @@ +// Copyright 2020 The Chromium 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 <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "third_party/zlib/zlib.h" + +// Fuzzer builds often have NDEBUG set, so roll our own assert macro. +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d Assert failed: %s\n", __FILE__, __LINE__, #cond); \ + exit(1); \ + } \ + } while (0) + +// Entry point for LibFuzzer. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Deflate data. + z_stream comp_strm; + comp_strm.zalloc = Z_NULL; + comp_strm.zfree = Z_NULL; + comp_strm.opaque = Z_NULL; + int ret = deflateInit(&comp_strm, Z_DEFAULT_COMPRESSION); + ASSERT(ret == Z_OK); + + size_t comp_buf_cap = deflateBound(&comp_strm, size); + uint8_t* comp_buf = (uint8_t*)malloc(comp_buf_cap); + ASSERT(comp_buf != nullptr); + comp_strm.next_out = comp_buf; + comp_strm.avail_out = comp_buf_cap; + comp_strm.next_in = (unsigned char*)data; + comp_strm.avail_in = size; + ret = deflate(&comp_strm, Z_FINISH); + ASSERT(ret == Z_STREAM_END); + size_t comp_sz = comp_buf_cap - comp_strm.avail_out; + + // Inflate comp_buf one chunk at a time. + z_stream decomp_strm; + decomp_strm.zalloc = Z_NULL; + decomp_strm.zfree = Z_NULL; + decomp_strm.opaque = Z_NULL; + ret = inflateInit(&decomp_strm); + ASSERT(ret == Z_OK); + decomp_strm.next_in = comp_buf; + decomp_strm.avail_in = comp_sz; + + while (decomp_strm.avail_in > 0) { + uint8_t decomp_buf[1024]; + decomp_strm.next_out = decomp_buf; + decomp_strm.avail_out = sizeof(decomp_buf); + ret = inflate(&decomp_strm, Z_FINISH); + ASSERT(ret == Z_OK || ret == Z_STREAM_END || ret == Z_BUF_ERROR); + + // Verify the output bytes. + size_t num_out = sizeof(decomp_buf) - decomp_strm.avail_out; + for (size_t i = 0; i < num_out; i++) { + ASSERT(decomp_buf[i] == data[decomp_strm.total_out - num_out + i]); + } + } + + ret = deflateEnd(&comp_strm); + ASSERT(ret == Z_OK); + free(comp_buf); + + inflateEnd(&decomp_strm); + ASSERT(ret == Z_OK); + + return 0; +} |