From 9de2322ed65732bb01b31772e3e1d130530e8c2e Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Sun, 5 Nov 2023 16:35:53 +0100 Subject: [PATCH 1/4] archive: Drop the GzipAndZlib zlib-decode mode We always know what kind of data we expect. --- archive/zlib.cpp | 6 ++---- archive/zlib.h | 1 - archive/zlib_test.cpp | 5 ----- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/archive/zlib.cpp b/archive/zlib.cpp index bb28d922..d2849b60 100644 --- a/archive/zlib.cpp +++ b/archive/zlib.cpp @@ -27,13 +27,11 @@ tl::expected zlib_decode(std::string_view data, ZlibMode // only the gzip format <...>. int const zlib_mode = [mode] { switch (mode) { - case ZlibMode::Zlib: - return 0; case ZlibMode::Gzip: return 15; - case ZlibMode::GzipAndZlib: default: - return 32; + case ZlibMode::Zlib: + return 0; } }(); constexpr int kWindowBits = 15; diff --git a/archive/zlib.h b/archive/zlib.h index 1471d49f..f5a22047 100644 --- a/archive/zlib.h +++ b/archive/zlib.h @@ -20,7 +20,6 @@ struct ZlibError { enum class ZlibMode { Zlib, Gzip, - GzipAndZlib, }; tl::expected zlib_decode(std::string_view, ZlibMode); diff --git a/archive/zlib_test.cpp b/archive/zlib_test.cpp index 5af0e85c..aa2b4520 100644 --- a/archive/zlib_test.cpp +++ b/archive/zlib_test.cpp @@ -37,10 +37,5 @@ int main() { expect_eq(zlib_decode(kGzippedCss, ZlibMode::Gzip), kExpected); }); - etest::test("zlib and gzip", [] { - expect_eq(zlib_decode(kZlibbedCss, ZlibMode::GzipAndZlib), kExpected); - expect_eq(zlib_decode(kGzippedCss, ZlibMode::GzipAndZlib), kExpected); - }); - return etest::run_all_tests(); } From 715d1b6bfcc321dffb493c50506f84f538ade4d6 Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Sun, 5 Nov 2023 17:19:00 +0100 Subject: [PATCH 2/4] archive: Fix crash when fed empty input ``` terminate called after throwing an instance of 'std::logic_error' what(): basic_string: construction from null is not valid ``` --- archive/zlib.cpp | 5 ++++- archive/zlib_test.cpp | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/archive/zlib.cpp b/archive/zlib.cpp index d2849b60..39a607a9 100644 --- a/archive/zlib.cpp +++ b/archive/zlib.cpp @@ -48,7 +48,10 @@ tl::expected zlib_decode(std::string_view data, ZlibMode s.avail_out = static_cast(buf.size()); int ret = inflate(&s, Z_NO_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) { - std::string msg = s.msg; + std::string msg; + if (s.msg != nullptr) { + msg = s.msg; + } inflateEnd(&s); return tl::unexpected{ZlibError{.message = std::move(msg), .code = ret}}; } diff --git a/archive/zlib_test.cpp b/archive/zlib_test.cpp index aa2b4520..a7fa0ae4 100644 --- a/archive/zlib_test.cpp +++ b/archive/zlib_test.cpp @@ -28,12 +28,16 @@ constexpr auto kZlibbedCss = int main() { etest::test("zlib", [] { - expect_eq(zlib_decode(kZlibbedCss, ZlibMode::Zlib), kExpected); + expect(!zlib_decode("", ZlibMode::Zlib).has_value()); expect(!zlib_decode(kGzippedCss, ZlibMode::Zlib).has_value()); + + expect_eq(zlib_decode(kZlibbedCss, ZlibMode::Zlib), kExpected); }); etest::test("gzip", [] { + expect(!zlib_decode("", ZlibMode::Gzip).has_value()); expect(!zlib_decode(kZlibbedCss, ZlibMode::Gzip), std::nullopt); + expect_eq(zlib_decode(kGzippedCss, ZlibMode::Gzip), kExpected); }); From b11eb89865410eeb7eb44a93a0e97adfabb73db3 Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Sun, 5 Nov 2023 17:15:16 +0100 Subject: [PATCH 3/4] archive/test: Remove explicit no-log-message argument --- archive/zlib_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archive/zlib_test.cpp b/archive/zlib_test.cpp index a7fa0ae4..29687d38 100644 --- a/archive/zlib_test.cpp +++ b/archive/zlib_test.cpp @@ -36,7 +36,7 @@ int main() { etest::test("gzip", [] { expect(!zlib_decode("", ZlibMode::Gzip).has_value()); - expect(!zlib_decode(kZlibbedCss, ZlibMode::Gzip), std::nullopt); + expect(!zlib_decode(kZlibbedCss, ZlibMode::Gzip).has_value()); expect_eq(zlib_decode(kGzippedCss, ZlibMode::Gzip), kExpected); }); From 500f3661c7f8e55ee60b0a6753371e882601ba7b Mon Sep 17 00:00:00 2001 From: Robin Linden Date: Sun, 5 Nov 2023 17:16:27 +0100 Subject: [PATCH 4/4] archive/test: Set up fuzz testing --- archive/BUILD | 17 +++++++++++++++-- archive/gzip_fuzz_test.cpp | 18 ++++++++++++++++++ archive/zlib_fuzz_test.cpp | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 archive/gzip_fuzz_test.cpp create mode 100644 archive/zlib_fuzz_test.cpp diff --git a/archive/BUILD b/archive/BUILD index 11c60473..dcea1e49 100644 --- a/archive/BUILD +++ b/archive/BUILD @@ -1,5 +1,6 @@ load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") -load("//bzl:copts.bzl", "HASTUR_COPTS") +load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") +load("//bzl:copts.bzl", "HASTUR_COPTS", "HASTUR_FUZZ_PLATFORMS") cc_library( name = "archive", @@ -27,4 +28,16 @@ cc_library( ":archive", "//etest", ], -) for src in glob(["*_test.cpp"])] +) for src in glob( + include = ["*_test.cpp"], + exclude = ["*_fuzz_test.cpp"], +)] + +[cc_fuzz_test( + name = src[:-4], + size = "small", + srcs = [src], + copts = HASTUR_COPTS, + target_compatible_with = HASTUR_FUZZ_PLATFORMS, + deps = [":archive"], +) for src in glob(["*_fuzz_test.cpp"])] diff --git a/archive/gzip_fuzz_test.cpp b/archive/gzip_fuzz_test.cpp new file mode 100644 index 00000000..2daddce9 --- /dev/null +++ b/archive/gzip_fuzz_test.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Robin Lindén +// +// SPDX-License-Identifier: BSD-2-Clause + +#include "archive/zlib.h" + +#include // NOLINT +#include // NOLINT +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size); // NOLINT + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) { + std::string_view input{reinterpret_cast(data), size}; + std::ignore = archive::zlib_decode(input, archive::ZlibMode::Gzip); + return 0; +} diff --git a/archive/zlib_fuzz_test.cpp b/archive/zlib_fuzz_test.cpp new file mode 100644 index 00000000..8d4d3564 --- /dev/null +++ b/archive/zlib_fuzz_test.cpp @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2023 Robin Lindén +// +// SPDX-License-Identifier: BSD-2-Clause + +#include "archive/zlib.h" + +#include // NOLINT +#include // NOLINT +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size); // NOLINT + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) { + std::string_view input{reinterpret_cast(data), size}; + std::ignore = archive::zlib_decode(input, archive::ZlibMode::Zlib); + return 0; +}