summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorHans Wennborg <hans@chromium.org>2020-08-04 11:38:55 +0000
committerCommit Bot <commit-bot@chromium.org>2020-08-04 11:38:55 +0000
commit493d429924861e336cfae702403a0c4145c173ae (patch)
treea41a3a5167bf039d0d73c1eaaa6acda9594dba71 /contrib
parenta21a4e8f27567b7c36f8274bf16ebca78b9a68ab (diff)
downloadzlib-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.h13
-rw-r--r--contrib/tests/fuzzers/BUILD.gn6
-rw-r--r--contrib/tests/fuzzers/streaming_inflate_fuzzer.cc74
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;
+}