diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-21 14:33:39 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-21 14:33:39 +0000 |
commit | 10f3ce513559013e3b02fc02c4d21a90c8430da7 (patch) | |
tree | 28fbe268bf51b1364ab6c0f0266a6f3c775fcf98 | |
parent | 6f4c43260c44f347a44ab240299150851a409d8f (diff) | |
parent | 3544c2604fc833c89a68e5fb4e563cf0a231cfb0 (diff) | |
download | zlib-android12-mainline-documentsui-release.tar.gz |
Snap for 7478028 from 3544c2604fc833c89a68e5fb4e563cf0a231cfb0 to mainline-documentsui-releaseandroid-mainline-12.0.0_r26android-mainline-12.0.0_r2aml_doc_310851020android12-mainline-documentsui-release
Change-Id: I5ef6c35786d3aabece874b3876d64e7df6e0b101
50 files changed, 2553 insertions, 270 deletions
@@ -1,9 +1,112 @@ +package { + default_applicable_licenses: ["external_zlib_license"], +} + +license { + name: "external_zlib_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-BSD", + "SPDX-license-identifier-Zlib", + ], + license_text: [ + "LICENSE", + ], +} + +srcs_opt = [ + "adler32_simd.c", + // See https://chromium-review.googlesource.com/749732. +// TODO: causes `atest org.apache.harmony.tests.java.util.zip.DeflaterTest` failures. +// "contrib/optimizations/inffast_chunk.c", +// "contrib/optimizations/inflate.c", + // This file doesn't build for non-neon, so it can't be in the main srcs. + "crc32_simd.c", +] + +cflags_arm = [ + // Since we're building for the platform, we claim to be Linux rather than + // Android so we use getauxval() directly instead of the NDK + // android_getCpuFeatures which isn't available to us anyway. + "-DARMV8_OS_LINUX", + // Testing with zlib_bench shows -O3 is a win for ARM but a bit of a wash + // for x86, so match the BUILD file in only enabling this for ARM. + "-O3", + // We need a non-NEON libz.a for the NDK, and cpu_features.c won't build + // without this. + "-DCPU_NO_SIMD", +] +cflags_arm_neon = [ + // Undo the -DCPU_NO_SIMD from the generic (non-NEON) ARM flags. + "-UCPU_NO_SIMD", + // We no longer support non-Neon platform builds, but the NDK just has one libz. + "-DADLER32_SIMD_NEON", +// TODO: causes `atest org.apache.harmony.tests.java.util.zip.DeflaterTest` failures. +// "-DINFLATE_CHUNK_SIMD_NEON", + // HWCAP_CRC32 is checked at runtime, so it's okay to turn crc32 + // acceleration on for both 32- and 64-bit. + "-DCRC32_ARMV8_CRC32", +] +cflags_arm64 = cflags_arm + cflags_arm_neon + +// The *host* x86 configuration (with *lower* CPU feature requirements). +cflags_x86 = [ + // See ARMV8_OS_LINUX above. + "-DX86_NOT_WINDOWS", +// TODO: see arm above. +// "-DINFLATE_CHUNK_SIMD_SSE2", + // Android's host CPU feature requirements are *lower* than the + // corresponding device CPU feature requirements, so it's easier to just + // say "no SIMD for you" rather than specificially disable SSSE3. + // We should have a conversation about that, but not until we at least have + // data on how many Studio users have CPUs that don't make the grade... + // https://issuetracker.google.com/171235570 + "-DCPU_NO_SIMD", +] +// The *device* x86 configuration (with *higher* CPU feature requirements). +cflags_android_x86 = [ + // Android's x86/x86-64 ABI includes SSE2 and SSSE3. + "-UCPU_NO_SIMD", + "-DADLER32_SIMD_SSSE3", + // PCLMUL isn't in the ABI, but it won't actually be used unless CPUID + // reports that the processor really does have the instruction. + "-mpclmul", + "-DCRC32_SIMD_SSE42_PCLMUL", +] +srcs_x86 = [ + "crc_folding.c", + "fill_window_sse.c", +] + srcs_opt + +// This optimization is applicable to arm64 and x86-64. +cflags_64 = ["-DINFLATE_CHUNK_READ_64LE"] + +libz_srcs = [ + "adler32.c", + "compress.c", + "cpu_features.c", + "crc32.c", + "deflate.c", + "gzclose.c", + "gzlib.c", + "gzread.c", + "gzwrite.c", + "infback.c", + "inffast.c", + "inflate.c", + "inftrees.c", + "trees.c", + "uncompr.c", + "zutil.c", +] + cc_defaults { name: "libz_defaults", cflags: [ - "-O3", + // We do support hidden visibility, so turn that on. "-DHAVE_HIDDEN", + // We do support const, so turn that on. "-DZLIB_CONST", "-Wall", "-Werror", @@ -12,36 +115,40 @@ cc_defaults { ], stl: "none", export_include_dirs: ["."], - srcs: [ - "adler32.c", - "compress.c", - "cpu_features.c", - "crc32.c", - "deflate.c", - "gzclose.c", - "gzlib.c", - "gzread.c", - "gzwrite.c", - "infback.c", - "inflate.c", - "inftrees.c", - "inffast.c", - "trees.c", - "uncompr.c", - "zutil.c", - ], + srcs: libz_srcs, arch: { arm: { - // measurements show that the ARM version of ZLib is about x1.17 faster - // than the thumb one... - // TODO: re-test with zlib_bench after SIMD is enabled. - instruction_set: "arm", - // TODO: This is to work around b/24465209. Remove after root cause // is fixed. pack_relocations: false, ldflags: ["-Wl,--hash-style=both"], + + cflags: cflags_arm, + neon: { + cflags: cflags_arm_neon, + srcs: srcs_opt, + } + }, + arm64: { + cflags: cflags_arm64 + cflags_64, + srcs: srcs_opt, + }, + x86: { + cflags: cflags_x86, + srcs: srcs_x86, + }, + x86_64: { + cflags: cflags_x86 + cflags_64, + srcs: srcs_x86, + }, + }, + target: { + android_x86: { + cflags: cflags_android_x86, + }, + android_x86_64: { + cflags: cflags_android_x86, }, }, } @@ -55,11 +162,13 @@ cc_library { static_ndk_lib: true, vendor_available: true, + product_available: true, vndk: { enabled: true, support_system_process: true, }, ramdisk_available: true, + vendor_ramdisk_available: true, recovery_available: true, native_bridge_supported: true, @@ -72,19 +181,41 @@ cc_library { }, }, -// TODO(b/155456180): make libz a stub-providing library by uncommenting below -// stubs: { -// versions: ["29", "30"], -// symbol_file: "libz.map.txt", -// }, - - apex_available: [ - "//apex_available:platform", - "com.android.art.debug", // from libdexfile - "com.android.art.release", - "com.android.bluetooth.updatable", - "com.android.runtime", + stubs: { + versions: [ + "29", + "30", + ], + symbol_file: "libz.map.txt", + }, +} + +// A more stable build of libz. Build configuration of this library should be +// the same for different targets. This is only used by imgdiff. + +cc_library { + name: "libz_stable", + visibility: [ + "//bootable/recovery/applypatch", + "//bootable/recovery/tests", ], + cflags: [ + // We do support hidden visibility, so turn that on. + "-DHAVE_HIDDEN", + // We do support const, so turn that on. + "-DZLIB_CONST", + "-Wall", + "-Werror", + "-Wno-unused", + "-Wno-unused-parameter", + ], + stl: "none", + export_include_dirs: ["."], + srcs: libz_srcs, + + host_supported: true, + vendor_available: true, + recovery_available: true, } cc_binary_host { @@ -101,41 +232,40 @@ cc_binary { cflags: ["-Wall", "-Werror"], host_supported: true, shared_libs: ["libz"], + // We build zlib_bench32 and zlib_bench64 so it's easy to test LP32. + compile_multilib: "both", + multilib: { + lib32: { suffix: "32", }, + lib64: { suffix: "64", }, + }, } -// This module is defined in development/ndk/Android.bp. Updating these headers -// to be usable for any API level is going to be some work (at the very least, -// there's a ZLIB_VERNUM that will need to be handled since early versions of -// Android did not have all the APIs that calling code will use if this is set -// to the current value. -// -// The NDK never updated the zlib headers when the platform updated, so until we -// solve this the NDK will continue shipping the old headers. -// -// ndk_headers { -// name: "libz_headers", -// from: "src", -// to: "", -// srcs: [ -// "src/zconf.h", -// "src/zlib.h", -// ], -// license: "NOTICE", -// } - -// TODO(b/155351357) remove this library and let libtextclassifier to use libz -// instead. -// libz_current allows modules building against the NDK to have access to zlib -// API that's not available from the NDK libz. -cc_library_static { - name: "libz_current", - defaults: ["libz_defaults"], - sdk_version: "current", +cc_test { + name: "zlib_tests", + srcs: [ + "contrib/tests/infcover.cc", + "contrib/tests/utils_unittest.cc", + "google/compression_utils_portable.cc", + ], + include_dirs: [ + "external/zlib/google", + // These tests include "gtest.h" rather than the usual "gtest/gtest.h". + "external/googletest/googletest/include/gtest/", + ], + shared_libs: ["libz"], + host_supported: true, + test_suites: ["device-tests"], +} - apex_available: [ - "//apex_available:platform", // indirectly from GoogleExtServices that gets installed to /system - "com.android.extservices", // indirectly via libtextclassifier +ndk_headers { + name: "libz_headers", + from: "", + to: "", + srcs: [ + "zconf.h", + "zlib.h", ], + license: "LICENSE", } ndk_library { @@ -4,6 +4,10 @@ import("//build/config/compiler/compiler.gni") +if (build_with_chromium) { + import("//testing/test.gni") +} + if (current_cpu == "arm" || current_cpu == "arm64") { import("//build/config/arm.gni") } @@ -14,10 +18,36 @@ config("zlib_config") { config("zlib_internal_config") { defines = [ "ZLIB_IMPLEMENTATION" ] + + if (!is_debug) { + # Build code using -O3, see: crbug.com/1084371. + configs = [ "//build/config/compiler:optimize_speed" ] + } + if (is_debug || use_libfuzzer) { + # Enable zlib's asserts in debug and fuzzer builds. + defines += [ "ZLIB_DEBUG" ] + } +} + +source_set("zlib_common_headers") { + sources = [ + "chromeconf.h", + "deflate.h", + "inffast.h", + "inffixed.h", + "inflate.h", + "inftrees.h", + "zconf.h", + "zlib.h", + "zutil.h", + ] } use_arm_neon_optimizations = false -if (current_cpu == "arm" || current_cpu == "arm64") { +if ((current_cpu == "arm" || current_cpu == "arm64") && + !(is_win && !is_clang)) { + # TODO(richard.townsend@arm.com): Optimizations temporarily disabled for + # Windows on Arm MSVC builds, see http://crbug.com/v8/10012. if (arm_use_neon) { use_arm_neon_optimizations = true } @@ -34,7 +64,9 @@ config("zlib_adler32_simd_config") { } else { defines += [ "X86_NOT_WINDOWS" ] } - } else if (use_arm_neon_optimizations) { + } + + if (use_arm_neon_optimizations) { defines = [ "ADLER32_SIMD_NEON" ] } } @@ -58,16 +90,13 @@ source_set("zlib_adler32_simd") { "adler32_simd.c", "adler32_simd.h", ] - if (!is_debug) { - # Use optimize_speed (-O3) to output the _smallest_ code. - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } } configs += [ ":zlib_internal_config" ] public_configs = [ ":zlib_adler32_simd_config" ] + + public_deps = [ ":zlib_common_headers" ] } if (use_arm_neon_optimizations) { @@ -81,6 +110,8 @@ if (use_arm_neon_optimizations) { defines += [ "ARMV8_OS_ANDROID" ] } else if (is_linux || is_chromeos) { defines += [ "ARMV8_OS_LINUX" ] + } else if (is_mac) { + defines += [ "ARMV8_OS_MACOS" ] } else if (is_fuchsia) { defines += [ "ARMV8_OS_FUCHSIA" ] } else if (is_win) { @@ -107,16 +138,13 @@ if (use_arm_neon_optimizations) { "crc32_simd.c", "crc32_simd.h", ] - - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } } configs += [ ":zlib_internal_config" ] public_configs = [ ":zlib_arm_crc32_config" ] + + public_deps = [ ":zlib_common_headers" ] } } @@ -131,6 +159,7 @@ config("zlib_inflate_chunk_simd_config") { if (use_arm_neon_optimizations) { defines = [ "INFLATE_CHUNK_SIMD_NEON" ] + if (current_cpu == "arm64") { defines += [ "INFLATE_CHUNK_READ_64LE" ] } @@ -149,22 +178,18 @@ source_set("zlib_inflate_chunk_simd") { "contrib/optimizations/inffast_chunk.h", "contrib/optimizations/inflate.c", ] - - if (use_arm_neon_optimizations && !is_debug) { - # Here we trade better performance on newer/bigger ARMv8 cores - # for less perf on ARMv7, per crbug.com/772870#c40 - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_speed" ] - } } + configs += [ ":zlib_internal_config" ] + + # Needed for MSVC, which is still supported by V8 and PDFium. zlib uses K&R C + # style function declarations, which triggers warning C4131. configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ - ":zlib_internal_config", - "//build/config/compiler:no_chromium_code", - ] + configs += [ "//build/config/compiler:no_chromium_code" ] public_configs = [ ":zlib_inflate_chunk_simd_config" ] + + public_deps = [ ":zlib_common_headers" ] } config("zlib_crc32_simd_config") { @@ -193,6 +218,16 @@ source_set("zlib_crc32_simd") { configs += [ ":zlib_internal_config" ] public_configs = [ ":zlib_crc32_simd_config" ] + public_deps = [ ":zlib_common_headers" ] +} + +config("zlib_x86_simd_config") { + if (use_x86_x64_optimizations) { + defines = [ + "CRC32_SIMD_SSE42_PCLMUL", + "DEFLATE_FILL_WINDOW_SSE2", + ] + } } source_set("zlib_x86_simd") { @@ -212,11 +247,11 @@ source_set("zlib_x86_simd") { } } - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ - ":zlib_internal_config", - "//build/config/compiler:no_chromium_code", - ] + configs += [ ":zlib_internal_config" ] + + public_configs = [ ":zlib_x86_simd_config" ] + + public_deps = [ ":zlib_common_headers" ] } config("zlib_warnings") { @@ -265,6 +300,7 @@ component("zlib") { defines = [] deps = [] + if (!use_x86_x64_optimizations && !use_arm_neon_optimizations) { # Apparently android_cronet bot builds with NEON disabled and # we also should disable optimizations for iOS@x86 (a.k.a. simulator). @@ -274,8 +310,8 @@ component("zlib") { if (is_ios) { # iOS@ARM is a special case where we always have NEON but don't check # for crypto extensions. - # TODO(cavalcantii): verify what is the current state of CPU features shipped - # on latest iOS devices. + # TODO(cavalcantii): verify what is the current state of CPU features + # shipped on latest iOS devices. defines += [ "ARM_OS_IOS" ] } @@ -295,6 +331,8 @@ component("zlib") { sources += [ "inflate.c" ] } + deps += [ ":zlib_x86_simd" ] + if (is_android) { import("//build/config/android/config.gni") if (defined(android_ndk_root) && android_ndk_root != "") { @@ -305,17 +343,17 @@ component("zlib") { } configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + public_configs = [ ":zlib_config" ] + configs += [ ":zlib_internal_config", - "//build/config/compiler:no_chromium_code", # Must be after no_chromium_code for warning flags to be ordered correctly. ":zlib_warnings", ] - public_configs = [ ":zlib_config" ] - - deps += [ ":zlib_x86_simd" ] allow_circular_includes_from = deps } @@ -347,7 +385,7 @@ static_library("minizip") { ] } - if (is_mac || is_ios || is_android || is_nacl) { + if (is_apple || is_android || is_nacl) { # Mac, Android and the BSDs don't have fopen64, ftello64, or fseeko64. We # use fopen, ftell, and fseek instead on these systems. defines = [ "USE_FILE32API" ] @@ -356,28 +394,59 @@ static_library("minizip") { deps = [ ":zlib" ] configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ - "//build/config/compiler:no_chromium_code", + configs += [ "//build/config/compiler:no_chromium_code" ] + + public_configs = [ ":zlib_config" ] + configs += [ # Must be after no_chromium_code for warning flags to be ordered correctly. ":minizip_warnings", ] - - public_configs = [ ":zlib_config" ] } executable("zlib_bench") { include_dirs = [ "." ] sources = [ "contrib/bench/zlib_bench.cc" ] - if (!is_debug) { configs -= [ "//build/config/compiler:default_optimization" ] configs += [ "//build/config/compiler:optimize_speed" ] } + deps = [ ":zlib" ] + configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] +} - deps = [ ":zlib" ] +if (build_with_chromium) { + test("zlib_unittests") { + testonly = true + + sources = [ + "contrib/tests/infcover.cc", + "contrib/tests/infcover.h", + "contrib/tests/run_all_unittests.cc", + "contrib/tests/utils_unittest.cc", + "google/compression_utils_unittest.cc", + "google/zip_reader_unittest.cc", + "google/zip_unittest.cc", + ] + + data = [ "google/test/data/" ] + + deps = [ + ":zlib", + "google:compression_utils", + "google:zip", + "//base/test:test_support", + "//testing/gtest", + ] + + include_dirs = [ + "//third_party/googletest/src/googletest/include/gtest", + ".", + "google", + ] + } } diff --git a/DIR_METADATA b/DIR_METADATA new file mode 100644 index 0000000..d366dc7 --- /dev/null +++ b/DIR_METADATA @@ -0,0 +1,3 @@ +monorail: { + component: "Internals" +} @@ -5,11 +5,11 @@ third_party { type: GIT value: "https://chromium.googlesource.com/chromium/src/third_party/zlib/" } - version: "b9b9a5af7cca2e683e5f2aead8418e5bf9d5a7d5" + version: "eb9ce8c993117f27ea0e5bccc0f2fee2a5323066" license_type: NOTICE last_upgrade_date { - year: 2020 - month: 2 - day: 7 + year: 2021 + month: 5 + day: 6 } } @@ -1 +0,0 @@ -LICENSE
\ No newline at end of file @@ -3,5 +3,3 @@ cavalcantii@chromium.org cblume@chromium.org mtklein@google.com scroggo@google.com - -# COMPONENT: Internals diff --git a/README.chromium b/README.chromium index 3d90f79..c3c1ef6 100644 --- a/README.chromium +++ b/README.chromium @@ -2,6 +2,7 @@ Name: zlib Short Name: zlib URL: http://zlib.net/ Version: 1.2.11 +CPEPrefix: cpe:/a:zlib:zlib:1.2.11 Security Critical: yes License: Custom license License File: LICENSE diff --git a/TEST_MAPPING b/TEST_MAPPING index 06f36fd..b2c8b36 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -3,18 +3,19 @@ { "name": "puffin_unittest" }, - // recovery_host_test (imgdiff) relies on zlib for patch generation. { - "name": "recovery_host_test", - "host": true + "name": "recovery_unit_test" }, { - "name": "recovery_unit_test" + "name": "update_engine_unittests" }, { "name": "ziparchive-tests" }, { + "name": "zlib_tests" + }, + { "name": "CtsLibcoreTestCases", "options": [ { @@ -74,10 +74,10 @@ uLong ZEXPORT adler32_z(adler, buf, len) unsigned n; #if defined(ADLER32_SIMD_SSSE3) - if (x86_cpu_enable_ssse3 && buf && len >= 64) + if (buf != Z_NULL && len >= 64 && x86_cpu_enable_ssse3) return adler32_simd_(adler, buf, len); #elif defined(ADLER32_SIMD_NEON) - if (buf && len >= 64) + if (buf != Z_NULL && len >= 64) return adler32_simd_(adler, buf, len); #endif diff --git a/chromeconf.h b/chromeconf.h index 666093d..5ecf29e 100644 --- a/chromeconf.h +++ b/chromeconf.h @@ -192,4 +192,8 @@ #define arm_check_features Cr_z_arm_check_features #define armv8_crc32_little Cr_z_armv8_crc32_little +/* Symbols added by cpu_features.c */ +#define cpu_check_features Cr_z_cpu_check_features +#define x86_cpu_enable_sse2 Cr_z_x86_cpu_enable_sse2 + #endif /* THIRD_PARTY_ZLIB_CHROMECONF_H_ */ diff --git a/contrib/bench/zlib_bench.cc b/contrib/bench/zlib_bench.cc index 5dcdef0..bd06ad3 100644 --- a/contrib/bench/zlib_bench.cc +++ b/contrib/bench/zlib_bench.cc @@ -15,7 +15,7 @@ * Note this code can be compiled outside of the Chromium build system against * the system zlib (-lz) with g++ or clang++ as follows: * - * g++|clang++ -O3 -Wall -std=c++11 -lstdc++ -lz zlib_bench.cc + * g++|clang++ -O3 -Wall -std=c++11 zlib_bench.cc -lstdc++ -lz */ #include <algorithm> @@ -38,7 +38,7 @@ void error_exit(const char* error, int code) { } inline char* string_data(std::string* s) { - return s->empty() ? 0 : &*s->begin(); + return s->empty() ? nullptr : &*s->begin(); } struct Data { @@ -99,10 +99,25 @@ const char* zlib_wrapper_name(zlib_wrapper type) { if (type == kWrapperZRAW) return "RAW"; error_exit("bad wrapper type", int(type)); - return 0; + return nullptr; +} + +static int zlib_strategy = Z_DEFAULT_STRATEGY; + +const char* zlib_level_strategy_name(int compression_level) { + if (compression_level == 0) + return ""; // strategy is meaningless at level 0 + if (zlib_strategy == Z_HUFFMAN_ONLY) + return "huffman "; + if (zlib_strategy == Z_RLE) + return "rle "; + if (zlib_strategy == Z_DEFAULT_STRATEGY) + return ""; + error_exit("bad strategy", zlib_strategy); + return nullptr; } -static int zlib_compression_level; +static int zlib_compression_level = Z_DEFAULT_COMPRESSION; void zlib_compress( const zlib_wrapper type, @@ -119,7 +134,7 @@ void zlib_compress( memset(&stream, 0, sizeof(stream)); int result = deflateInit2(&stream, zlib_compression_level, Z_DEFLATED, - zlib_stream_wrapper_type(type), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + zlib_stream_wrapper_type(type), MAX_MEM_LEVEL, zlib_strategy); if (result != Z_OK) error_exit("deflateInit2 failed", result); @@ -185,7 +200,12 @@ void zlib_file(const char* name, const zlib_wrapper type) { const auto file = read_file_data_or_exit(name); const int length = static_cast<int>(file.size); const char* data = file.data.get(); - printf("%-40s :\n", name); + + /* + * Report compression strategy and file name. + */ + const char* strategy = zlib_level_strategy_name(zlib_compression_level); + printf("%s%-40s :\n", strategy, name); /* * Chop the data into blocks. @@ -276,18 +296,20 @@ static int argn = 1; char* get_option(int argc, char* argv[], const char* option) { if (argn < argc) - return !strcmp(argv[argn], option) ? argv[argn++] : 0; - return 0; + return !strcmp(argv[argn], option) ? argv[argn++] : nullptr; + return nullptr; } bool get_compression(int argc, char* argv[], int* value) { if (argn < argc) - *value = atoi(argv[argn++]); - return *value >= 1 && *value <= 9; + *value = isdigit(argv[argn][0]) ? atoi(argv[argn++]) : -1; + return *value >= 0 && *value <= 9; } +const char* options = "gzip|zlib|raw [--compression 0:9] [--huffman|--rle]"; + void usage_exit(const char* program) { - printf("usage: %s gzip|zlib|raw [--compression 1:9] files...\n", program); + printf("usage: %s %s files...", program, options); exit(1); } @@ -302,10 +324,18 @@ int main(int argc, char* argv[]) { else usage_exit(argv[0]); - if (!get_option(argc, argv, "--compression")) - zlib_compression_level = Z_DEFAULT_COMPRESSION; - else if (!get_compression(argc, argv, &zlib_compression_level)) - usage_exit(argv[0]); + while (argn < argc && argv[argn][0] == '-') { + if (get_option(argc, argv, "--compression")) { + if (!get_compression(argc, argv, &zlib_compression_level)) + usage_exit(argv[0]); + } else if (get_option(argc, argv, "--huffman")) { + zlib_strategy = Z_HUFFMAN_ONLY; + } else if (get_option(argc, argv, "--rle")) { + zlib_strategy = Z_RLE; + } else { + usage_exit(argv[0]); + } + } if (argn >= argc) usage_exit(argv[0]); diff --git a/contrib/minizip/iowin32.c b/contrib/minizip/iowin32.c index 246ceb9..c6bc314 100644 --- a/contrib/minizip/iowin32.c +++ b/contrib/minizip/iowin32.c @@ -31,14 +31,12 @@ #define _WIN32_WINNT 0x601 #endif -#if _WIN32_WINNT >= _WIN32_WINNT_WIN8 -// see Include/shared/winapifamily.h in the Windows Kit -#if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API))) -#if WINAPI_FAMILY_ONE_PARTITION(WINAPI_FAMILY, WINAPI_PARTITION_APP) +#if !defined(IOWIN32_USING_WINRT_API) +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +// Windows Store or Universal Windows Platform #define IOWIN32_USING_WINRT_API 1 #endif #endif -#endif voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); diff --git a/contrib/optimizations/chunkcopy.h b/contrib/optimizations/chunkcopy.h index 38ba0ed..9c0b7cb 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); } @@ -406,6 +419,26 @@ static inline unsigned char FAR* chunkcopy_lapped_safe( return chunkcopy_lapped_relaxed(out, dist, len); } +/* TODO(cavalcanti): see crbug.com/1110083. */ +static inline unsigned char FAR* chunkcopy_safe_ugly(unsigned char FAR* out, + unsigned dist, + unsigned len, + unsigned char FAR* limit) { +#if defined(__GNUC__) && !defined(__clang__) + /* Speed is the same as using chunkcopy_safe + w/ GCC on ARM (tested gcc 6.3 and 7.5) and avoids + undefined behavior. + */ + return chunkcopy_core_safe(out, out - dist, len, limit); +#elif defined(__clang__) && defined(ARMV8_OS_ANDROID) && !defined(__aarch64__) + /* Seems to perform better on 32bit (i.e. Android). */ + return chunkcopy_core_safe(out, out - dist, len, limit); +#else + /* Seems to perform better on 64bit. */ + return chunkcopy_lapped_safe(out, dist, len, limit); +#endif +} + /* * The chunk-copy code above deals with writing the decoded DEFLATE data to * the output with SIMD methods to increase decode speed. Reading the input diff --git a/contrib/optimizations/inffast_chunk.c b/contrib/optimizations/inffast_chunk.c index 4099edf..4bacbc4 100644 --- a/contrib/optimizations/inffast_chunk.c +++ b/contrib/optimizations/inffast_chunk.c @@ -276,7 +276,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ the main copy is near the end. */ out = chunkunroll_relaxed(out, &dist, &len); - out = chunkcopy_safe(out, out - dist, len, limit); + out = chunkcopy_safe_ugly(out, dist, len, limit); } else { /* from points to window, so there is no risk of overlapping pointers requiring memset-like behaviour diff --git a/contrib/optimizations/insert_string.h b/contrib/optimizations/insert_string.h index 1826601..9f634ae 100644 --- a/contrib/optimizations/insert_string.h +++ b/contrib/optimizations/insert_string.h @@ -4,45 +4,51 @@ * Use of this source code is governed by a BSD-style license that can be * found in the Chromium source repository LICENSE file. */ -#ifdef _MSC_VER + +#if defined(_MSC_VER) #define INLINE __inline #else #define INLINE inline #endif #include "cpu_features.h" -/* Optimized insert_string block */ -#if defined(CRC32_SIMD_SSE42_PCLMUL) || defined(CRC32_ARMV8_CRC32) -#define TARGET_CPU_WITH_CRC + // clang-format off #if defined(CRC32_SIMD_SSE42_PCLMUL) - /* Required to make MSVC bot build pass. */ - #include <smmintrin.h> - #if defined(__GNUC__) || defined(__clang__) - #undef TARGET_CPU_WITH_CRC + #include <smmintrin.h> /* Required to make MSVC bot build pass. */ + + #if defined(__clang__) || defined(__GNUC__) #define TARGET_CPU_WITH_CRC __attribute__((target("sse4.2"))) + #else + #define TARGET_CPU_WITH_CRC #endif #define _cpu_crc32_u32 _mm_crc32_u32 #elif defined(CRC32_ARMV8_CRC32) #if defined(__clang__) - #undef TARGET_CPU_WITH_CRC #define __crc32cw __builtin_arm_crc32cw + #elif defined(__GNUC__) + #define __crc32cw __builtin_aarch64_crc32cw #endif - #define _cpu_crc32_u32 __crc32cw - - #if defined(__aarch64__) + #if defined(__aarch64__) && defined(__clang__) #define TARGET_CPU_WITH_CRC __attribute__((target("crc"))) - #else // !defined(__aarch64__) + #elif defined(__aarch64__) && defined(__GNUC__) + #define TARGET_CPU_WITH_CRC __attribute__((target("+crc"))) + #elif defined(__clang__) // !defined(__aarch64__) #define TARGET_CPU_WITH_CRC __attribute__((target("armv8-a,crc"))) #endif // defined(__aarch64__) + + #define _cpu_crc32_u32 __crc32cw + #endif // clang-format on + +#if defined(TARGET_CPU_WITH_CRC) + TARGET_CPU_WITH_CRC -local INLINE Pos insert_string_optimized(deflate_state* const s, - const Pos str) { +local INLINE Pos insert_string_simd(deflate_state* const s, const Pos str) { Pos ret; unsigned *ip, val, h = 0; @@ -64,7 +70,8 @@ local INLINE Pos insert_string_optimized(deflate_state* const s, s->prev[str & s->w_mask] = ret; return ret; } -#endif /* Optimized insert_string block */ + +#endif // TARGET_CPU_WITH_CRC /* =========================================================================== * Update a hash value with the given input byte @@ -99,24 +106,22 @@ local INLINE Pos insert_string_c(deflate_state* const s, const Pos str) { } local INLINE Pos insert_string(deflate_state* const s, const Pos str) { -/* String dictionary insertion: faster symbol hashing has a positive impact - * on data compression speeds (around 20% on Intel and 36% on Arm Cortex big - * cores). - * A misfeature is that the generated compressed output will differ from - * vanilla zlib (even though it is still valid 'DEFLATE-d' content). +/* insert_string_simd string dictionary insertion: this SIMD symbol hashing + * significantly improves data compression speed. * - * We offer here a way to disable the optimization if there is the expectation - * that compressed content should match when compared to vanilla zlib. + * Note: the generated compressed output is a valid DEFLATE stream but will + * differ from vanilla zlib output ... */ -#if !defined(CHROMIUM_ZLIB_NO_CASTAGNOLI) - /* TODO(cavalcantii): unify CPU features code. */ -#if defined(CRC32_ARMV8_CRC32) - if (arm_cpu_enable_crc32) - return insert_string_optimized(s, str); -#elif defined(CRC32_SIMD_SSE42_PCLMUL) +#if defined(CHROMIUM_ZLIB_NO_CASTAGNOLI) +/* ... so this build-time option can used to disable the SIMD symbol hasher + * if matching vanilla zlib DEFLATE output is required. + */ (;) /* FALLTHOUGH */ +#elif defined(TARGET_CPU_WITH_CRC) && defined(CRC32_SIMD_SSE42_PCLMUL) if (x86_cpu_enable_simd) - return insert_string_optimized(s, str); -#endif + return insert_string_simd(s, str); +#elif defined(TARGET_CPU_WITH_CRC) && defined(CRC32_ARMV8_CRC32) + if (arm_cpu_enable_crc32) + return insert_string_simd(s, str); #endif return insert_string_c(s, str); } diff --git a/contrib/tests/DEPS b/contrib/tests/DEPS new file mode 100644 index 0000000..4275174 --- /dev/null +++ b/contrib/tests/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+testing/gtest", + "+base", +] diff --git a/contrib/tests/OWNERS b/contrib/tests/OWNERS index 9a2fb6f..aa6a2d1 100644 --- a/contrib/tests/OWNERS +++ b/contrib/tests/OWNERS @@ -1 +1,2 @@ cblume@chromium.org +cavalcantii@chromium.org 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/OWNERS b/contrib/tests/fuzzers/OWNERS index 6397ce6..9a2fb6f 100644 --- a/contrib/tests/fuzzers/OWNERS +++ b/contrib/tests/fuzzers/OWNERS @@ -1,2 +1 @@ cblume@chromium.org -mmoroz@chromium.org diff --git a/contrib/tests/fuzzers/deflate_fuzzer.cc b/contrib/tests/fuzzers/deflate_fuzzer.cc index 6098ff1..c00e715 100644 --- a/contrib/tests/fuzzers/deflate_fuzzer.cc +++ b/contrib/tests/fuzzers/deflate_fuzzer.cc @@ -2,46 +2,73 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <fuzzer/FuzzedDataProvider.h> #include <stddef.h> #include <stdint.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <cassert> #include <vector> #include "third_party/zlib/zlib.h" -static Bytef buffer[256 * 1024] = {0}; +// 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) { - // zlib's deflate requires non-zero input sizes - if (!size) - return 0; - - // We need to strip the 'const' for zlib. - std::vector<unsigned char> input_buffer{data, data+size}; - - uLongf buffer_length = static_cast<uLongf>(sizeof(buffer)); + FuzzedDataProvider fdp(data, size); + int level = fdp.PickValueInArray({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + int windowBits = fdp.PickValueInArray({9, 10, 11, 12, 13, 14, 15}); + int memLevel = fdp.PickValueInArray({1, 2, 3, 4, 5, 6, 7, 8, 9}); + int strategy = fdp.PickValueInArray( + {Z_DEFAULT_STRATEGY, Z_FILTERED, Z_HUFFMAN_ONLY, Z_RLE, Z_FIXED}); + std::vector<uint8_t> src = fdp.ConsumeRemainingBytes<uint8_t>(); z_stream stream; - stream.next_in = input_buffer.data(); - stream.avail_in = size; - stream.total_in = size; - stream.next_out = buffer; - stream.avail_out = buffer_length; - stream.total_out = buffer_length; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; - if (Z_OK != deflateInit(&stream, Z_DEFAULT_COMPRESSION)) { - deflateEnd(&stream); - assert(false); + // Compress the data one byte at a time to exercise the streaming code. + int ret = + deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + ASSERT(ret == Z_OK); + std::vector<uint8_t> compressed(src.size() * 2 + 1000); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + for (uint8_t b : src) { + stream.next_in = &b; + stream.avail_in = 1; + ret = deflate(&stream, Z_NO_FLUSH); + ASSERT(ret == Z_OK); } - - auto deflate_result = deflate(&stream, Z_NO_FLUSH); + stream.next_in = Z_NULL; + stream.avail_in = 0; + ret = deflate(&stream, Z_FINISH); + ASSERT(ret == Z_STREAM_END); + compressed.resize(compressed.size() - stream.avail_out); deflateEnd(&stream); - if (Z_OK != deflate_result) - assert(false); + + // Verify that the data decompresses correctly. + ret = inflateInit2(&stream, windowBits); + ASSERT(ret == Z_OK); + // Make room for at least one byte so it's never empty. + std::vector<uint8_t> decompressed(src.size() + 1); + stream.next_in = compressed.data(); + stream.avail_in = compressed.size(); + stream.next_out = decompressed.data(); + stream.avail_out = decompressed.size(); + ret = inflate(&stream, Z_FINISH); + ASSERT(ret == Z_STREAM_END); + decompressed.resize(decompressed.size() - stream.avail_out); + inflateEnd(&stream); + + ASSERT(decompressed == src); return 0; } 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; +} diff --git a/contrib/tests/infcover.cc b/contrib/tests/infcover.cc new file mode 100644 index 0000000..c5300a5 --- /dev/null +++ b/contrib/tests/infcover.cc @@ -0,0 +1,684 @@ +/* infcover.c -- test zlib's inflate routines with full code coverage + * Copyright (C) 2011, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* to use, do: ./configure --cover && make cover */ +// clang-format off +#include "infcover.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "zlib.h" + +/* get definition of internal structure so we can mess with it (see pull()), + and so we can call inflate_trees() (see cover5()) */ +#define ZLIB_INTERNAL +#include "inftrees.h" +#include "inflate.h" + +/* XXX: use C++ streams instead of printf/fputs/etc due to portability + * as type sizes can vary between platforms. + */ +#include <iostream> +#define local static + +/* XXX: hacking C assert and plugging into GTest. */ +#include "gtest.h" +#if defined(assert) +#undef assert +#define assert EXPECT_TRUE +#endif + +/* XXX: handle what is a reserved word in C++. */ +#define try try_f + +/* -- memory tracking routines -- */ + +/* + These memory tracking routines are provided to zlib and track all of zlib's + allocations and deallocations, check for LIFO operations, keep a current + and high water mark of total bytes requested, optionally set a limit on the + total memory that can be allocated, and when done check for memory leaks. + + They are used as follows: + + z_stream strm; + mem_setup(&strm) initializes the memory tracking and sets the + zalloc, zfree, and opaque members of strm to use + memory tracking for all zlib operations on strm + mem_limit(&strm, limit) sets a limit on the total bytes requested -- a + request that exceeds this limit will result in an + allocation failure (returns NULL) -- setting the + limit to zero means no limit, which is the default + after mem_setup() + mem_used(&strm, "msg") prints to stderr "msg" and the total bytes used + mem_high(&strm, "msg") prints to stderr "msg" and the high water mark + mem_done(&strm, "msg") ends memory tracking, releases all allocations + for the tracking as well as leaked zlib blocks, if + any. If there was anything unusual, such as leaked + blocks, non-FIFO frees, or frees of addresses not + allocated, then "msg" and information about the + problem is printed to stderr. If everything is + normal, nothing is printed. mem_done resets the + strm members to Z_NULL to use the default memory + allocation routines on the next zlib initialization + using strm. + */ + +/* these items are strung together in a linked list, one for each allocation */ +struct mem_item { + void *ptr; /* pointer to allocated memory */ + size_t size; /* requested size of allocation */ + struct mem_item *next; /* pointer to next item in list, or NULL */ +}; + +/* this structure is at the root of the linked list, and tracks statistics */ +struct mem_zone { + struct mem_item *first; /* pointer to first item in list, or NULL */ + size_t total, highwater; /* total allocations, and largest total */ + size_t limit; /* memory allocation limit, or 0 if no limit */ + int notlifo, rogue; /* counts of non-LIFO frees and rogue frees */ +}; + +/* memory allocation routine to pass to zlib */ +local void *mem_alloc(void *mem, unsigned count, unsigned size) +{ + void *ptr; + struct mem_item *item; + struct mem_zone *zone = static_cast<struct mem_zone *>(mem); + size_t len = count * (size_t)size; + + /* induced allocation failure */ + if (zone == NULL || (zone->limit && zone->total + len > zone->limit)) + return NULL; + + /* perform allocation using the standard library, fill memory with a + non-zero value to make sure that the code isn't depending on zeros */ + ptr = malloc(len); + if (ptr == NULL) + return NULL; + memset(ptr, 0xa5, len); + + /* create a new item for the list */ + item = static_cast<struct mem_item *>(malloc(sizeof(struct mem_item))); + if (item == NULL) { + free(ptr); + return NULL; + } + item->ptr = ptr; + item->size = len; + + /* insert item at the beginning of the list */ + item->next = zone->first; + zone->first = item; + + /* update the statistics */ + zone->total += item->size; + if (zone->total > zone->highwater) + zone->highwater = zone->total; + + /* return the allocated memory */ + return ptr; +} + +/* memory free routine to pass to zlib */ +local void mem_free(void *mem, void *ptr) +{ + struct mem_item *item, *next; + struct mem_zone *zone = static_cast<struct mem_zone *>(mem); + + /* if no zone, just do a free */ + if (zone == NULL) { + free(ptr); + return; + } + + /* point next to the item that matches ptr, or NULL if not found -- remove + the item from the linked list if found */ + next = zone->first; + if (next) { + if (next->ptr == ptr) + zone->first = next->next; /* first one is it, remove from list */ + else { + do { /* search the linked list */ + item = next; + next = item->next; + } while (next != NULL && next->ptr != ptr); + if (next) { /* if found, remove from linked list */ + item->next = next->next; + zone->notlifo++; /* not a LIFO free */ + } + + } + } + + /* if found, update the statistics and free the item */ + if (next) { + zone->total -= next->size; + free(next); + } + + /* if not found, update the rogue count */ + else + zone->rogue++; + + /* in any case, do the requested free with the standard library function */ + free(ptr); +} + +/* set up a controlled memory allocation space for monitoring, set the stream + parameters to the controlled routines, with opaque pointing to the space */ +local void mem_setup(z_stream *strm) +{ + struct mem_zone *zone; + + zone = static_cast<struct mem_zone *>(malloc(sizeof(struct mem_zone))); + assert(zone != NULL); + zone->first = NULL; + zone->total = 0; + zone->highwater = 0; + zone->limit = 0; + zone->notlifo = 0; + zone->rogue = 0; + strm->opaque = zone; + strm->zalloc = mem_alloc; + strm->zfree = mem_free; +} + +/* set a limit on the total memory allocation, or 0 to remove the limit */ +local void mem_limit(z_stream *strm, size_t limit) +{ + struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + + zone->limit = limit; +} + +/* show the current total requested allocations in bytes */ +local void mem_used(z_stream *strm, const char *prefix) +{ + struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + + std::cout << prefix << ": " << zone->total << " allocated" << std::endl; +} + +/* show the high water allocation in bytes */ +local void mem_high(z_stream *strm, const char *prefix) +{ + struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + + std::cout << prefix << ": " << zone->highwater << " high water mark" << std::endl; +} + +/* release the memory allocation zone -- if there are any surprises, notify */ +local void mem_done(z_stream *strm, const char *prefix) +{ + int count = 0; + struct mem_item *item, *next; + struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + + /* show high water mark */ + mem_high(strm, prefix); + + /* free leftover allocations and item structures, if any */ + item = zone->first; + while (item != NULL) { + free(item->ptr); + next = item->next; + free(item); + item = next; + count++; + } + + /* issue alerts about anything unexpected */ + if (count || zone->total) + std::cout << "** " << prefix << ": " + << zone->total << " bytes in " + << count << " blocks not freed" + << std::endl; + + if (zone->notlifo) + std::cout << "** " << prefix << ": " + << zone->notlifo << " frees not LIFO" + << std::endl; + + if (zone->rogue) + std::cout << "** " << prefix << ": " + << zone->rogue << " frees not recognized" + << std::endl; + + /* free the zone and delete from the stream */ + free(zone); + strm->opaque = Z_NULL; + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; +} + +/* -- inflate test routines -- */ + +/* Decode a hexadecimal string, set *len to length, in[] to the bytes. This + decodes liberally, in that hex digits can be adjacent, in which case two in + a row writes a byte. Or they can be delimited by any non-hex character, + where the delimiters are ignored except when a single hex digit is followed + by a delimiter, where that single digit writes a byte. The returned data is + allocated and must eventually be freed. NULL is returned if out of memory. + If the length is not needed, then len can be NULL. */ +local unsigned char *h2b(const char *hex, unsigned *len) +{ + unsigned char *in, *re; + unsigned next, val; + + in = static_cast<unsigned char *>(malloc((strlen(hex) + 1) >> 1)); + if (in == NULL) + return NULL; + next = 0; + val = 1; + do { + if (*hex >= '0' && *hex <= '9') + val = (val << 4) + *hex - '0'; + else if (*hex >= 'A' && *hex <= 'F') + val = (val << 4) + *hex - 'A' + 10; + else if (*hex >= 'a' && *hex <= 'f') + val = (val << 4) + *hex - 'a' + 10; + else if (val != 1 && val < 32) /* one digit followed by delimiter */ + val += 240; /* make it look like two digits */ + if (val > 255) { /* have two digits */ + in[next++] = val & 0xff; /* save the decoded byte */ + val = 1; /* start over */ + } + } while (*hex++); /* go through the loop with the terminating null */ + if (len != NULL) + *len = next; + re = static_cast<unsigned char *>(realloc(in, next)); + return re == NULL ? in : re; +} + +/* generic inflate() run, where hex is the hexadecimal input data, what is the + text to include in an error message, step is how much input data to feed + inflate() on each call, or zero to feed it all, win is the window bits + parameter to inflateInit2(), len is the size of the output buffer, and err + is the error code expected from the first inflate() call (the second + inflate() call is expected to return Z_STREAM_END). If win is 47, then + header information is collected with inflateGetHeader(). If a zlib stream + is looking for a dictionary, then an empty dictionary is provided. + inflate() is run until all of the input data is consumed. */ +local void inf(const char *hex, const char *what, unsigned step, int win, unsigned len, + int err) +{ + int ret; + unsigned have; + unsigned char *in, *out; + z_stream strm, copy; + gz_header head; + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, win); + if (ret != Z_OK) { + mem_done(&strm, what); + return; + } + out = static_cast<unsigned char *>(malloc(len)); assert(out != NULL); + if (win == 47) { + head.extra = out; + head.extra_max = len; + head.name = out; + head.name_max = len; + head.comment = out; + head.comm_max = len; + ret = inflateGetHeader(&strm, &head); assert(ret == Z_OK); + } + in = h2b(hex, &have); assert(in != NULL); + if (step == 0 || step > have) + step = have; + strm.avail_in = step; + have -= step; + strm.next_in = in; + do { + strm.avail_out = len; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); assert(err == 9 || ret == err); + if (ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_NEED_DICT) + break; + if (ret == Z_NEED_DICT) { + ret = inflateSetDictionary(&strm, in, 1); + assert(ret == Z_DATA_ERROR); + mem_limit(&strm, 1); + ret = inflateSetDictionary(&strm, out, 0); + assert(ret == Z_MEM_ERROR); + mem_limit(&strm, 0); + ((struct inflate_state *)strm.state)->mode = DICT; + ret = inflateSetDictionary(&strm, out, 0); + assert(ret == Z_OK); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_BUF_ERROR); + } + ret = inflateCopy(©, &strm); assert(ret == Z_OK); + ret = inflateEnd(©); assert(ret == Z_OK); + err = 9; /* don't care next time around */ + have += strm.avail_in; + strm.avail_in = step > have ? have : step; + have -= strm.avail_in; + } while (strm.avail_in); + free(in); + free(out); + ret = inflateReset2(&strm, -8); assert(ret == Z_OK); + ret = inflateEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, what); +} + +/* cover all of the lines in inflate.c up to inflate() */ +void cover_support(void) +{ + int ret; + z_stream strm; + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); assert(ret == Z_OK); + mem_used(&strm, "inflate init"); + ret = inflatePrime(&strm, 5, 31); assert(ret == Z_OK); + ret = inflatePrime(&strm, -1, 0); assert(ret == Z_OK); + ret = inflateSetDictionary(&strm, Z_NULL, 0); + assert(ret == Z_STREAM_ERROR); + ret = inflateEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, "prime"); + + inf("63 0", "force window allocation", 0, -15, 1, Z_OK); + inf("63 18 5", "force window replacement", 0, -8, 259, Z_OK); + inf("63 18 68 30 d0 0 0", "force split window update", 4, -8, 259, Z_OK); + inf("3 0", "use fixed blocks", 0, -15, 1, Z_STREAM_END); + inf("", "bad window size", 0, 1, 0, Z_STREAM_ERROR); + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit_(&strm, ZLIB_VERSION - 1, (int)sizeof(z_stream)); + assert(ret == Z_VERSION_ERROR); + mem_done(&strm, "wrong version"); + + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); assert(ret == Z_OK); + ret = inflateEnd(&strm); assert(ret == Z_OK); + std::cout << "inflate built-in memory routines" << std::endl;; +} + +/* cover all inflate() header and trailer cases and code after inflate() */ +void cover_wrap(void) +{ + int ret; + z_stream strm, copy; + unsigned char dict[257]; + + ret = inflate(Z_NULL, 0); assert(ret == Z_STREAM_ERROR); + ret = inflateEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); + ret = inflateCopy(Z_NULL, Z_NULL); assert(ret == Z_STREAM_ERROR); + std::cout << "inflate bad parameters" << std::endl; + + inf("1f 8b 0 0", "bad gzip method", 0, 31, 0, Z_DATA_ERROR); + inf("1f 8b 8 80", "bad gzip flags", 0, 31, 0, Z_DATA_ERROR); + inf("77 85", "bad zlib method", 0, 15, 0, Z_DATA_ERROR); + inf("8 99", "set window size from header", 0, 0, 0, Z_OK); + inf("78 9c", "bad zlib window size", 0, 8, 0, Z_DATA_ERROR); + inf("78 9c 63 0 0 0 1 0 1", "check adler32", 0, 15, 1, Z_STREAM_END); + inf("1f 8b 8 1e 0 0 0 0 0 0 1 0 0 0 0 0 0", "bad header crc", 0, 47, 1, + Z_DATA_ERROR); + inf("1f 8b 8 2 0 0 0 0 0 0 1d 26 3 0 0 0 0 0 0 0 0 0", "check gzip length", + 0, 47, 0, Z_STREAM_END); + inf("78 90", "bad zlib header check", 0, 47, 0, Z_DATA_ERROR); + inf("8 b8 0 0 0 1", "need dictionary", 0, 8, 0, Z_NEED_DICT); + inf("78 9c 63 0", "compute adler32", 0, 15, 1, Z_OK); + + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, -8); + strm.avail_in = 2; + strm.next_in = (Bytef *)"\x63"; + strm.avail_out = 1; + strm.next_out = (Bytef *)&ret; + mem_limit(&strm, 1); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); + mem_limit(&strm, 0); + memset(dict, 0, 257); + ret = inflateSetDictionary(&strm, dict, 257); + assert(ret == Z_OK); + mem_limit(&strm, (sizeof(struct inflate_state) << 1) + 256); + ret = inflatePrime(&strm, 16, 0); assert(ret == Z_OK); + strm.avail_in = 2; + strm.next_in = (Bytef *)"\x80"; + ret = inflateSync(&strm); assert(ret == Z_DATA_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_STREAM_ERROR); + strm.avail_in = 4; + strm.next_in = (Bytef *)"\0\0\xff\xff"; + ret = inflateSync(&strm); assert(ret == Z_OK); + (void)inflateSyncPoint(&strm); + ret = inflateCopy(©, &strm); assert(ret == Z_MEM_ERROR); + mem_limit(&strm, 0); + ret = inflateUndermine(&strm, 1); assert(ret == Z_DATA_ERROR); + (void)inflateMark(&strm); + ret = inflateEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, "miscellaneous, force memory errors"); +} + +/* input and output functions for inflateBack() */ +local unsigned pull(void *desc, unsigned char **buf) +{ + static unsigned int next = 0; + static unsigned char dat[] = {0x63, 0, 2, 0}; + struct inflate_state *state; + + if (desc == Z_NULL) { + next = 0; + return 0; /* no input (already provided at next_in) */ + } + state = reinterpret_cast<struct inflate_state *>(((z_stream *)desc)->state); + if (state != Z_NULL) + state->mode = SYNC; /* force an otherwise impossible situation */ + return next < sizeof(dat) ? (*buf = dat + next++, 1) : 0; +} + +local int push(void *desc, unsigned char *buf, unsigned len) +{ + buf += len; + return desc != Z_NULL; /* force error if desc not null */ +} + +/* cover inflateBack() up to common deflate data cases and after those */ +void cover_back(void) +{ + int ret; + z_stream strm; + unsigned char win[32768]; + + ret = inflateBackInit_(Z_NULL, 0, win, 0, 0); + assert(ret == Z_VERSION_ERROR); + ret = inflateBackInit(Z_NULL, 0, win); assert(ret == Z_STREAM_ERROR); + ret = inflateBack(Z_NULL, Z_NULL, Z_NULL, Z_NULL, Z_NULL); + assert(ret == Z_STREAM_ERROR); + ret = inflateBackEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); + std::cout << "inflateBack bad parameters" << std::endl;; + + mem_setup(&strm); + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + strm.avail_in = 2; + strm.next_in = (Bytef *)"\x03"; + ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); + assert(ret == Z_STREAM_END); + /* force output error */ + strm.avail_in = 3; + strm.next_in = (Bytef *)"\x63\x00"; + ret = inflateBack(&strm, pull, Z_NULL, push, &strm); + assert(ret == Z_BUF_ERROR); + /* force mode error by mucking with state */ + ret = inflateBack(&strm, pull, &strm, push, Z_NULL); + assert(ret == Z_STREAM_ERROR); + ret = inflateBackEnd(&strm); assert(ret == Z_OK); + mem_done(&strm, "inflateBack bad state"); + + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + ret = inflateBackEnd(&strm); assert(ret == Z_OK); + std::cout << "inflateBack built-in memory routines" << std::endl;; +} + +/* do a raw inflate of data in hexadecimal with both inflate and inflateBack */ +local int try(const char *hex, const char *id, int err) +{ + int ret; + unsigned len, size; + unsigned char *in, *out, *win; + char *prefix; + z_stream strm; + + /* convert to hex */ + in = h2b(hex, &len); + assert(in != NULL); + + /* allocate work areas */ + size = len << 3; + out = static_cast<unsigned char *>(malloc(size)); + assert(out != NULL); + win = static_cast<unsigned char *>(malloc(32768)); + assert(win != NULL); + prefix = static_cast<char *>(malloc(strlen(id) + 6)); + assert(prefix != NULL); + + /* first with inflate */ + strcpy(prefix, id); + strcat(prefix, "-late"); + mem_setup(&strm); + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, err < 0 ? 47 : -15); + assert(ret == Z_OK); + strm.avail_in = len; + strm.next_in = in; + do { + strm.avail_out = size; + strm.next_out = out; + ret = inflate(&strm, Z_TREES); + assert(ret != Z_STREAM_ERROR && ret != Z_MEM_ERROR); + if (ret == Z_DATA_ERROR || ret == Z_NEED_DICT) + break; + } while (strm.avail_in || strm.avail_out == 0); + if (err) { + assert(ret == Z_DATA_ERROR); + assert(strcmp(id, strm.msg) == 0); + } + inflateEnd(&strm); + mem_done(&strm, prefix); + + /* then with inflateBack */ + if (err >= 0) { + strcpy(prefix, id); + strcat(prefix, "-back"); + mem_setup(&strm); + ret = inflateBackInit(&strm, 15, win); + assert(ret == Z_OK); + strm.avail_in = len; + strm.next_in = in; + ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); + assert(ret != Z_STREAM_ERROR); + if (err) { + assert(ret == Z_DATA_ERROR); + assert(strcmp(id, strm.msg) == 0); + } + inflateBackEnd(&strm); + mem_done(&strm, prefix); + } + + /* clean up */ + free(prefix); + free(win); + free(out); + free(in); + return ret; +} + +/* cover deflate data cases in both inflate() and inflateBack() */ +void cover_inflate(void) +{ + try("0 0 0 0 0", "invalid stored block lengths", 1); + try("3 0", "fixed", 0); + try("6", "invalid block type", 1); + try("1 1 0 fe ff 0", "stored", 0); + try("fc 0 0", "too many length or distance symbols", 1); + try("4 0 fe ff", "invalid code lengths set", 1); + try("4 0 24 49 0", "invalid bit length repeat", 1); + try("4 0 24 e9 ff ff", "invalid bit length repeat", 1); + try("4 0 24 e9 ff 6d", "invalid code -- missing end-of-block", 1); + try("4 80 49 92 24 49 92 24 71 ff ff 93 11 0", + "invalid literal/lengths set", 1); + try("4 80 49 92 24 49 92 24 f b4 ff ff c3 84", "invalid distances set", 1); + try("4 c0 81 8 0 0 0 0 20 7f eb b 0 0", "invalid literal/length code", 1); + try("2 7e ff ff", "invalid distance code", 1); + try("c c0 81 0 0 0 0 0 90 ff 6b 4 0", "invalid distance too far back", 1); + + /* also trailer mismatch just in inflate() */ + try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 1", "incorrect data check", -1); + try("1f 8b 8 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 1", + "incorrect length check", -1); + try("5 c0 21 d 0 0 0 80 b0 fe 6d 2f 91 6c", "pull 17", 0); + try("5 e0 81 91 24 cb b2 2c 49 e2 f 2e 8b 9a 47 56 9f fb fe ec d2 ff 1f", + "long code", 0); + try("ed c0 1 1 0 0 0 40 20 ff 57 1b 42 2c 4f", "length extra", 0); + try("ed cf c1 b1 2c 47 10 c4 30 fa 6f 35 1d 1 82 59 3d fb be 2e 2a fc f c", + "long distance and extra", 0); + try("ed c0 81 0 0 0 0 80 a0 fd a9 17 a9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " + "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6", "window end", 0); + inf("2 8 20 80 0 3 0", "inflate_fast TYPE return", 0, -15, 258, + Z_STREAM_END); + inf("63 18 5 40 c 0", "window wrap", 3, -8, 300, Z_OK); +} + +/* XXX(cavalcantii): fix linking error due inflate_table. */ +/* cover remaining lines in inftrees.c */ +/* void cover_trees(void) */ +/* { */ +/* int ret; */ +/* unsigned bits; */ +/* unsigned short lens[16], work[16]; */ +/* code *next, table[ENOUGH_DISTS]; */ + +/* /\* we need to call inflate_table() directly in order to manifest not- */ +/* enough errors, since zlib insures that enough is always enough *\/ */ +/* for (bits = 0; bits < 15; bits++) */ +/* lens[bits] = (unsigned short)(bits + 1); */ +/* lens[15] = 15; */ +/* next = table; */ +/* bits = 15; */ +/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ +/* assert(ret == 1); */ +/* next = table; */ +/* bits = 1; */ +/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ +/* assert(ret == 1); */ +/* fputs("inflate_table not enough errors\n", stderr); */ +/* } */ + +/* cover remaining inffast.c decoding and window copying */ +void cover_fast(void) +{ + inf("e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68" + " ff 7f 0f 0 0 0", "fast length extra bits", 0, -8, 258, Z_DATA_ERROR); + inf("25 fd 81 b5 6d 59 b6 6a 49 ea af 35 6 34 eb 8c b9 f6 b9 1e ef 67 49" + " 50 fe ff ff 3f 0 0", "fast distance extra bits", 0, -8, 258, + Z_DATA_ERROR); + inf("3 7e 0 0 0 0 0", "fast invalid distance code", 0, -8, 258, + Z_DATA_ERROR); + inf("1b 7 0 0 0 0 0", "fast invalid literal/length code", 0, -8, 258, + Z_DATA_ERROR); + inf("d c7 1 ae eb 38 c 4 41 a0 87 72 de df fb 1f b8 36 b1 38 5d ff ff 0", + "fast 2nd level codes and too far back", 0, -8, 258, Z_DATA_ERROR); + inf("63 18 5 8c 10 8 0 0 0 0", "very common case", 0, -8, 259, Z_OK); + inf("63 60 60 18 c9 0 8 18 18 18 26 c0 28 0 29 0 0 0", + "contiguous and wrap around window", 6, -8, 259, Z_OK); + inf("63 0 3 0 0 0 0 0", "copy direct from output", 0, -8, 259, + Z_STREAM_END); +} + +// clang-format on diff --git a/contrib/tests/infcover.h b/contrib/tests/infcover.h new file mode 100644 index 0000000..b3e112f --- /dev/null +++ b/contrib/tests/infcover.h @@ -0,0 +1,11 @@ +#ifndef __INF_COVER_H__ +#define __INF_COVER_H__ + +void cover_support(void); +void cover_wrap(void); +void cover_back(void); +void cover_inflate(void); +void cover_trees(void); +void cover_fast(void); + +#endif diff --git a/contrib/tests/run_all_unittests.cc b/contrib/tests/run_all_unittests.cc new file mode 100644 index 0000000..65ed57e --- /dev/null +++ b/contrib/tests/run_all_unittests.cc @@ -0,0 +1,14 @@ +// 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 "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" + +int main(int argc, char** argv) { + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, argv, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/contrib/tests/utils_unittest.cc b/contrib/tests/utils_unittest.cc new file mode 100644 index 0000000..468421e --- /dev/null +++ b/contrib/tests/utils_unittest.cc @@ -0,0 +1,521 @@ +// 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 Chromium source repository LICENSE file. + +#include "infcover.h" + +#include <cstddef> +#include <vector> + +#include "compression_utils_portable.h" +#include "gtest.h" +#include "zlib.h" + +void TestPayloads(size_t input_size, zlib_internal::WrapperType type) { + std::vector<unsigned char> input; + input.reserve(input_size); + for (size_t i = 1; i <= input_size; ++i) + input.push_back(i & 0xff); + + // If it is big enough for GZIP, will work for other wrappers. + std::vector<unsigned char> compressed( + zlib_internal::GzipExpectedCompressedSize(input.size())); + std::vector<unsigned char> decompressed(input.size()); + + // Libcores's java/util/zip/Deflater default settings: ZLIB, + // DEFAULT_COMPRESSION and DEFAULT_STRATEGY. + unsigned long compressed_size = static_cast<unsigned long>(compressed.size()); + int result = zlib_internal::CompressHelper( + type, compressed.data(), &compressed_size, input.data(), input.size(), + Z_DEFAULT_COMPRESSION, nullptr, nullptr); + ASSERT_EQ(result, Z_OK); + + unsigned long decompressed_size = + static_cast<unsigned long>(decompressed.size()); + result = zlib_internal::UncompressHelper(type, decompressed.data(), + &decompressed_size, + compressed.data(), compressed_size); + ASSERT_EQ(result, Z_OK); + EXPECT_EQ(input, decompressed); +} + +TEST(ZlibTest, ZlibWrapper) { + // Minimal ZLIB wrapped short stream size is about 8 bytes. + for (size_t i = 1; i < 1024; ++i) + TestPayloads(i, zlib_internal::WrapperType::ZLIB); +} + +TEST(ZlibTest, GzipWrapper) { + // GZIP should be 12 bytes bigger than ZLIB wrapper. + for (size_t i = 1; i < 1024; ++i) + TestPayloads(i, zlib_internal::WrapperType::GZIP); +} + +TEST(ZlibTest, RawWrapper) { + // RAW has no wrapper (V8 Blobs is a known user), size + // should be payload_size + 2 for short payloads. + for (size_t i = 1; i < 1024; ++i) + TestPayloads(i, zlib_internal::WrapperType::ZRAW); +} + +TEST(ZlibTest, InflateCover) { + cover_support(); + cover_wrap(); + cover_back(); + cover_inflate(); + // TODO(cavalcantii): enable this last test. + // cover_trees(); + cover_fast(); +} + +TEST(ZlibTest, DeflateStored) { + const int no_compression = 0; + const zlib_internal::WrapperType type = zlib_internal::WrapperType::GZIP; + std::vector<unsigned char> input(1 << 10, 42); + std::vector<unsigned char> compressed( + zlib_internal::GzipExpectedCompressedSize(input.size())); + std::vector<unsigned char> decompressed(input.size()); + unsigned long compressed_size = static_cast<unsigned long>(compressed.size()); + int result = zlib_internal::CompressHelper( + type, compressed.data(), &compressed_size, input.data(), input.size(), + no_compression, nullptr, nullptr); + ASSERT_EQ(result, Z_OK); + + unsigned long decompressed_size = + static_cast<unsigned long>(decompressed.size()); + result = zlib_internal::UncompressHelper(type, decompressed.data(), + &decompressed_size, + compressed.data(), compressed_size); + ASSERT_EQ(result, Z_OK); + EXPECT_EQ(input, decompressed); +} + +TEST(ZlibTest, StreamingInflate) { + uint8_t comp_buf[4096], decomp_buf[4096]; + z_stream comp_strm, decomp_strm; + int ret; + + std::vector<uint8_t> src; + for (size_t i = 0; i < 1000; i++) { + for (size_t j = 0; j < 40; j++) { + src.push_back(j); + } + } + + // Deflate src into comp_buf. + comp_strm.zalloc = Z_NULL; + comp_strm.zfree = Z_NULL; + comp_strm.opaque = Z_NULL; + ret = deflateInit(&comp_strm, Z_BEST_COMPRESSION); + ASSERT_EQ(ret, Z_OK); + comp_strm.next_out = comp_buf; + comp_strm.avail_out = sizeof(comp_buf); + comp_strm.next_in = src.data(); + comp_strm.avail_in = src.size(); + ret = deflate(&comp_strm, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + size_t comp_sz = sizeof(comp_buf) - comp_strm.avail_out; + + // Inflate comp_buf one 4096-byte buffer at a time. + decomp_strm.zalloc = Z_NULL; + decomp_strm.zfree = Z_NULL; + decomp_strm.opaque = Z_NULL; + ret = inflateInit(&decomp_strm); + ASSERT_EQ(ret, Z_OK); + decomp_strm.next_in = comp_buf; + decomp_strm.avail_in = comp_sz; + + while (decomp_strm.avail_in > 0) { + decomp_strm.next_out = decomp_buf; + decomp_strm.avail_out = sizeof(decomp_buf); + ret = inflate(&decomp_strm, Z_FINISH); + ASSERT_TRUE(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++) { + EXPECT_EQ(decomp_buf[i], src[decomp_strm.total_out - num_out + i]); + } + } + + // Cleanup memory (i.e. makes ASAN bot happy). + ret = deflateEnd(&comp_strm); + EXPECT_EQ(ret, Z_OK); + ret = inflateEnd(&decomp_strm); + EXPECT_EQ(ret, Z_OK); +} + +TEST(ZlibTest, CRCHashBitsCollision) { + // The CRC32c of the hex sequences 2a,14,14,14 and 2a,14,db,14 have the same + // lower 9 bits. Since longest_match doesn't check match[2], a bad match could + // be chosen when the number of hash bits is <= 9. For this reason, the number + // of hash bits must be set higher, regardless of the memlevel parameter, when + // using CRC32c hashing for string matching. See https://crbug.com/1113596 + + std::vector<uint8_t> src = { + // Random byte; zlib doesn't match at offset 0. + 123, + + // This will look like 5-byte match. + 0x2a, + 0x14, + 0xdb, + 0x14, + 0x15, + + // Offer a 4-byte match to bump the next expected match length to 5. + 0x2a, + 0x14, + 0x14, + 0x14, + + 0x2a, + 0x14, + 0x14, + 0x14, + 0x15, + }; + + z_stream stream; + stream.zalloc = nullptr; + stream.zfree = nullptr; + + // Using a low memlevel to try to reduce the number of hash bits. Negative + // windowbits means raw deflate, i.e. without the zlib header. + int ret = deflateInit2(&stream, /*comp level*/ 2, /*method*/ Z_DEFLATED, + /*windowbits*/ -15, /*memlevel*/ 2, + /*strategy*/ Z_DEFAULT_STRATEGY); + ASSERT_EQ(ret, Z_OK); + std::vector<uint8_t> compressed(100, '\0'); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + stream.next_in = src.data(); + stream.avail_in = src.size(); + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + compressed.resize(compressed.size() - stream.avail_out); + deflateEnd(&stream); + + ret = inflateInit2(&stream, /*windowbits*/ -15); + ASSERT_EQ(ret, Z_OK); + std::vector<uint8_t> decompressed(src.size(), '\0'); + stream.next_in = compressed.data(); + stream.avail_in = compressed.size(); + stream.next_out = decompressed.data(); + stream.avail_out = decompressed.size(); + ret = inflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + EXPECT_EQ(0U, stream.avail_out); + inflateEnd(&stream); + + EXPECT_EQ(src, decompressed); +} + +TEST(ZlibTest, CRCHashAssert) { + // The CRC32c of the hex sequences ff,ff,5e,6f and ff,ff,13,ff have the same + // lower 15 bits. This means longest_match's assert that match[2] == scan[2] + // won't hold. However, such hash collisions are only possible when one of the + // other four bytes also mismatch. This tests that zlib's assert handles this + // case. + + std::vector<uint8_t> src = { + // Random byte; zlib doesn't match at offset 0. + 123, + + // This has the same hash as the last byte sequence, and the first two and + // last two bytes match; though the third and the fourth don't. + 0xff, + 0xff, + 0x5e, + 0x6f, + 0x12, + 0x34, + + // Offer a 5-byte match to bump the next expected match length to 6 + // (because the two first and two last bytes need to match). + 0xff, + 0xff, + 0x13, + 0xff, + 0x12, + + 0xff, + 0xff, + 0x13, + 0xff, + 0x12, + 0x34, + }; + + z_stream stream; + stream.zalloc = nullptr; + stream.zfree = nullptr; + + int ret = deflateInit2(&stream, /*comp level*/ 5, /*method*/ Z_DEFLATED, + /*windowbits*/ -15, /*memlevel*/ 8, + /*strategy*/ Z_DEFAULT_STRATEGY); + ASSERT_EQ(ret, Z_OK); + std::vector<uint8_t> compressed(100, '\0'); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + stream.next_in = src.data(); + stream.avail_in = src.size(); + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + compressed.resize(compressed.size() - stream.avail_out); + deflateEnd(&stream); + + ret = inflateInit2(&stream, /*windowbits*/ -15); + ASSERT_EQ(ret, Z_OK); + std::vector<uint8_t> decompressed(src.size(), '\0'); + stream.next_in = compressed.data(); + stream.avail_in = compressed.size(); + stream.next_out = decompressed.data(); + stream.avail_out = decompressed.size(); + ret = inflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + EXPECT_EQ(0U, stream.avail_out); + inflateEnd(&stream); + + EXPECT_EQ(src, decompressed); +} + +// Fuzzer generated. +static const uint8_t checkMatchCrashData[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, + 0x6e, 0x6e, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x01, 0x39, 0x6e, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x6e, + 0x00, 0x00, 0x0a, 0x9a, 0x00, 0x00, 0x6e, 0x6e, 0x6e, 0x2a, 0x00, 0x00, + 0x00, 0xd5, 0xf0, 0x00, 0x81, 0x02, 0xf3, 0xfd, 0xff, 0xab, 0xf3, 0x6e, + 0x7e, 0x04, 0x5b, 0xf6, 0x2a, 0x2c, 0xf8, 0x00, 0x54, 0xf3, 0xa5, 0x0e, + 0xfd, 0x6e, 0xff, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xa4, 0x0b, 0xa5, 0x2a, 0x0d, 0x10, 0x01, 0x26, 0xf6, 0x04, 0x0e, + 0xff, 0x6e, 0x6e, 0x6e, 0x76, 0x00, 0x00, 0x87, 0x01, 0xfe, 0x0d, 0xb6, + 0x6e, 0x6e, 0xf7, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfd, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x9b, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x6e, 0xff, 0xff, 0x00, + 0x00, 0xd5, 0xf0, 0x00, 0xff, 0x40, 0x7e, 0x0b, 0xa5, 0x10, 0x67, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x40, 0x7e, 0x0b, 0xa5, 0x10, 0x67, + 0x7e, 0x32, 0x6e, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x40, 0x0b, 0xa5, + 0x10, 0x67, 0x01, 0xfe, 0x0d, 0xb6, 0x2a, 0x00, 0x00, 0x58, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3d, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd6, 0x2d, 0x2d, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x66, 0x8a, 0x8a, 0x8a, 0xee, 0x1d, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0xee, 0x0a, 0x00, 0x00, 0x00, 0x54, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf3, 0x00, 0x00, 0xff, 0xff, 0x23, 0x7e, 0x00, 0x1e, + 0x00, 0x00, 0xd5, 0xf0, 0x00, 0xff, 0x40, 0x0b, 0xa5, 0x10, 0x67, 0x01, + 0xfe, 0x0d, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x2d, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x0e, + 0xfb, 0x00, 0x10, 0x24, 0x00, 0x00, 0xfb, 0xff, 0x00, 0x00, 0xff, 0x1f, + 0xb3, 0x00, 0x04, 0x3d, 0x00, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x01, 0x45, 0x3d, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x11, 0x21, 0x00, 0x1e, + 0x00, 0x0c, 0xb3, 0xfe, 0x0e, 0xee, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6e, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x6e, 0x00, + 0x00, 0x87, 0x00, 0x33, 0x38, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x00, 0xff, 0xff, 0xff, 0x04, 0x3f, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xff, 0x00, 0x31, 0x13, 0x13, 0x13, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x30, 0x83, 0x33, + 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0xff, 0xff, 0x7d, 0xff, 0x00, 0x01, + 0x10, 0x0d, 0x2a, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x11, + 0x21, 0x00, 0xa5, 0x00, 0x68, 0x68, 0x68, 0x67, 0x00, 0x00, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x68, 0x68, 0x68, 0x68, 0x00, 0x00, 0xfa, 0xff, 0xff, + 0x03, 0x01, 0xff, 0x02, 0x00, 0x00, 0x68, 0x68, 0x68, 0x68, 0x0a, 0x10, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x06, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfa, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0x00, 0x06, 0x04, + 0x00, 0xf8, 0xff, 0xff, 0x00, 0x01, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x00, 0x06, 0x04, 0x6e, + 0x7e, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, + 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x6e, 0x6e, 0x6e, + 0x00, 0x01, 0x38, 0xd5, 0xf0, 0x00, 0x00, 0x2a, 0xfe, 0x04, 0x5b, 0x0d, + 0xfd, 0x6e, 0x92, 0x28, 0xf9, 0xfb, 0xff, 0x07, 0xd2, 0xd6, 0x2d, 0x2d, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0xc2, 0x91, 0x00, 0x5b, 0xef, 0xde, 0xf2, 0x6e, 0x6e, 0xfd, + 0x0c, 0x02, 0x91, 0x62, 0x91, 0xfd, 0x6e, 0x6e, 0xd3, 0x06, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, + 0xd5, 0xf0, 0x00, 0xff, 0x00, 0x00, 0x31, 0x13, 0x13, 0x13, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x13, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x6e, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x09, 0x00, 0x6a, 0x24, 0x26, 0x30, 0x01, 0x2e, 0x2a, 0xfe, + 0x04, 0x5b, 0x0d, 0xfd, 0x6e, 0x6e, 0xd7, 0x06, 0x6e, 0x6e, 0x6e, 0x00, + 0x00, 0xb1, 0xb1, 0xb1, 0xb1, 0x00, 0x00, 0x00, 0x6e, 0x5b, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x24, 0x2a, 0x6e, 0x5c, 0x24, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x05, 0x00, 0x00, 0x00, 0x5d, 0x10, 0x6e, 0x6e, 0xa5, 0x2f, 0x00, 0x00, + 0x95, 0x87, 0x00, 0x6e}; + +TEST(ZlibTest, CheckMatchCrash) { + // See https://crbug.com/1113142. + z_stream stream; + stream.zalloc = nullptr; + stream.zfree = nullptr; + + // Low windowbits to hit window sliding also with a relatively small input. + int ret = deflateInit2(&stream, /*comp level*/ 5, /*method*/ Z_DEFLATED, + /*windowbits*/ -9, /*memlevel*/ 8, + /*strategy*/ Z_DEFAULT_STRATEGY); + ASSERT_EQ(ret, Z_OK); + + uint8_t compressed[sizeof(checkMatchCrashData) * 2]; + stream.next_out = compressed; + stream.avail_out = sizeof(compressed); + + for (size_t i = 0; i < sizeof(checkMatchCrashData); i++) { + ASSERT_GT(stream.avail_out, 0U); + stream.next_in = (uint8_t*)&checkMatchCrashData[i]; + stream.avail_in = 1; + ret = deflate(&stream, Z_NO_FLUSH); + ASSERT_EQ(ret, Z_OK); + } + + stream.next_in = nullptr; + stream.avail_in = 0; + ASSERT_GT(stream.avail_out, 0U); + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + size_t compressed_sz = sizeof(compressed) - stream.avail_out; + deflateEnd(&stream); + + uint8_t decompressed[sizeof(checkMatchCrashData)]; + ret = inflateInit2(&stream, -15); + ASSERT_EQ(ret, Z_OK); + stream.next_in = compressed; + stream.avail_in = compressed_sz; + stream.next_out = decompressed; + stream.avail_out = sizeof(decompressed); + ret = inflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + inflateEnd(&stream); + ASSERT_EQ( + memcmp(checkMatchCrashData, decompressed, sizeof(checkMatchCrashData)), + 0); +} + +TEST(ZlibTest, DeflateRLEUninitUse) { + // MSan would complain about use of uninitialized values in deflate_rle if the + // window isn't zero-initialized. See crbug.com/1137613. Similar problems + // exist in other places in zlib, e.g. longest_match (crbug.com/1144420) but + // we don't have as nice test cases. + + int level = 9; + int windowBits = 9; + int memLevel = 8; + int strategy = Z_RLE; + const std::vector<uint8_t> src{ + 0x31, 0x64, 0x38, 0x32, 0x30, 0x32, 0x30, 0x36, 0x65, 0x35, 0x38, 0x35, + 0x32, 0x61, 0x30, 0x36, 0x65, 0x35, 0x32, 0x66, 0x30, 0x34, 0x38, 0x37, + 0x61, 0x31, 0x38, 0x36, 0x37, 0x37, 0x31, 0x39, 0x0a, 0x65, 0x62, 0x00, + 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, + 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, + 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0x95, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0e, 0x0a, 0x54, 0x52, + 0x58, 0x56, 0xab, 0x26, 0x13, 0x53, 0x5a, 0xb5, 0x30, 0xbb, 0x96, 0x44, + 0x80, 0xe6, 0xc5, 0x0a, 0xd0, 0x47, 0x7a, 0xa0, 0x4e, 0xbe, 0x30, 0xdc, + 0xa1, 0x08, 0x54, 0xe1, 0x51, 0xd1, 0xea, 0xef, 0xdb, 0xa1, 0x2d, 0xb4, + 0xb9, 0x58, 0xb1, 0x2f, 0xf0, 0xae, 0xbc, 0x07, 0xd1, 0xba, 0x7f, 0x14, + 0xa4, 0xde, 0x99, 0x7f, 0x4d, 0x3e, 0x25, 0xd9, 0xef, 0xee, 0x4f, 0x38, + 0x7b, 0xaf, 0x3f, 0x6b, 0x53, 0x5a, 0xcb, 0x1f, 0x97, 0xb5, 0x43, 0xa3, + 0xe8, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, + 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, + 0x62, 0x00, 0x9f, 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, + 0x9f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x3c, + 0x73, 0x70, 0x23, 0x87, 0xec, 0xf8, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc1, 0x00, 0x00, 0x9f, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, + 0xff, 0xc6, 0xc6, 0xc6, 0xff, 0x09, 0x00, 0x62, 0x00, 0x9f, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + }; + + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + + // Compress the data one byte at a time to exercise the streaming code. + int ret = + deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + ASSERT_EQ(ret, Z_OK); + std::vector<uint8_t> compressed(src.size() * 2 + 1000); + stream.next_out = compressed.data(); + stream.avail_out = compressed.size(); + for (uint8_t b : src) { + stream.next_in = &b; + stream.avail_in = 1; + ret = deflate(&stream, Z_NO_FLUSH); + ASSERT_EQ(ret, Z_OK); + } + stream.next_in = Z_NULL; + stream.avail_in = 0; + ret = deflate(&stream, Z_FINISH); + ASSERT_EQ(ret, Z_STREAM_END); + deflateEnd(&stream); +} diff --git a/cpu_features.c b/cpu_features.c index 8a25dd2..70f01be 100644 --- a/cpu_features.c +++ b/cpu_features.c @@ -17,11 +17,20 @@ /* TODO(cavalcantii): remove checks for x86_flags on deflate. */ +#if defined(ARMV8_OS_MACOS) +/* crc32 is a baseline feature in ARMv8.1-A, and macOS running on arm64 is new + * enough that this can be assumed without runtime detection. */ +int ZLIB_INTERNAL arm_cpu_enable_crc32 = 1; +#else int ZLIB_INTERNAL arm_cpu_enable_crc32 = 0; +#endif int ZLIB_INTERNAL arm_cpu_enable_pmull = 0; +int ZLIB_INTERNAL x86_cpu_enable_sse2 = 0; int ZLIB_INTERNAL x86_cpu_enable_ssse3 = 0; int ZLIB_INTERNAL x86_cpu_enable_simd = 0; +#ifndef CPU_NO_SIMD + #if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_FUCHSIA) #include <pthread.h> #endif @@ -43,15 +52,23 @@ int ZLIB_INTERNAL x86_cpu_enable_simd = 0; #error cpu_features.c CPU feature detection in not defined for your platform #endif -#if !defined(CPU_NO_SIMD) && !defined(ARM_OS_IOS) +#if !defined(CPU_NO_SIMD) && !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS) static void _cpu_check_features(void); #endif -#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_FUCHSIA) || defined(X86_NOT_WINDOWS) +#if defined(ARMV8_OS_ANDROID) || defined(ARMV8_OS_LINUX) || defined(ARMV8_OS_MACOS) || defined(ARMV8_OS_FUCHSIA) || defined(X86_NOT_WINDOWS) +#if !defined(ARMV8_OS_MACOS) +// _cpu_check_features() doesn't need to do anything on mac/arm since all +// features are known at build time, so don't call it. +// Do provide cpu_check_features() (with a no-op implementation) so that we +// don't have to make all callers of it check for mac/arm. static pthread_once_t cpu_check_inited_once = PTHREAD_ONCE_INIT; +#endif void ZLIB_INTERNAL cpu_check_features(void) { +#if !defined(ARMV8_OS_MACOS) pthread_once(&cpu_check_inited_once, _cpu_check_features); +#endif } #elif defined(ARMV8_OS_WINDOWS) || defined(X86_WINDOWS) static INIT_ONCE cpu_check_inited_once = INIT_ONCE_STATIC_INIT; @@ -72,7 +89,7 @@ void ZLIB_INTERNAL cpu_check_features(void) * iOS@ARM is a special case where we always have NEON but don't check * for crypto extensions. */ -#ifndef ARM_OS_IOS +#if !defined(ARMV8_OS_MACOS) && !defined(ARM_OS_IOS) /* * See http://bit.ly/2CcoEsr for run-time detection of ARM features and also * crbug.com/931275 for android_getCpuFeatures() use in the Android sandbox. @@ -125,16 +142,20 @@ static void _cpu_check_features(void) int x86_cpu_has_sse42; int x86_cpu_has_pclmulqdq; int abcd[4]; + #ifdef _MSC_VER __cpuid(abcd, 1); #else __cpuid(1, abcd[0], abcd[1], abcd[2], abcd[3]); #endif + x86_cpu_has_sse2 = abcd[3] & 0x4000000; x86_cpu_has_ssse3 = abcd[2] & 0x000200; x86_cpu_has_sse42 = abcd[2] & 0x100000; x86_cpu_has_pclmulqdq = abcd[2] & 0x2; + x86_cpu_enable_sse2 = x86_cpu_has_sse2; + x86_cpu_enable_ssse3 = x86_cpu_has_ssse3; x86_cpu_enable_simd = x86_cpu_has_sse2 && @@ -143,3 +164,4 @@ static void _cpu_check_features(void) } #endif #endif +#endif diff --git a/cpu_features.h b/cpu_features.h index 2a4a797..c7b15c5 100644 --- a/cpu_features.h +++ b/cpu_features.h @@ -11,6 +11,7 @@ */ extern int arm_cpu_enable_crc32; extern int arm_cpu_enable_pmull; +extern int x86_cpu_enable_sse2; extern int x86_cpu_enable_ssse3; extern int x86_cpu_enable_simd; @@ -497,7 +497,7 @@ uLong ZEXPORT crc32_combine64(crc1, crc2, len2) ZLIB_INTERNAL void crc_reset(deflate_state *const s) { -#ifdef ADLER32_SIMD_SSSE3 +#ifdef CRC32_SIMD_SSE42_PCLMUL if (x86_cpu_enable_simd) { crc_fold_init(s); return; @@ -508,7 +508,7 @@ ZLIB_INTERNAL void crc_reset(deflate_state *const s) ZLIB_INTERNAL void crc_finalize(deflate_state *const s) { -#ifdef ADLER32_SIMD_SSSE3 +#ifdef CRC32_SIMD_SSE42_PCLMUL if (x86_cpu_enable_simd) s->strm->adler = crc_fold_512to32(s); #endif @@ -516,7 +516,7 @@ ZLIB_INTERNAL void crc_finalize(deflate_state *const s) ZLIB_INTERNAL void copy_with_crc(z_streamp strm, Bytef *dst, long size) { -#ifdef ADLER32_SIMD_SSSE3 +#ifdef CRC32_SIMD_SSE42_PCLMUL if (x86_cpu_enable_simd) { crc_fold_copy(strm->state, dst, strm->next_in, size); return; diff --git a/crc_folding.c b/crc_folding.c index 48d7774..ee31d49 100644 --- a/crc_folding.c +++ b/crc_folding.c @@ -18,6 +18,8 @@ #include "deflate.h" +#ifdef CRC32_SIMD_SSE42_PCLMUL + #include <inttypes.h> #include <emmintrin.h> #include <immintrin.h> @@ -283,7 +285,7 @@ ZLIB_INTERNAL void crc_fold_copy(deflate_state *const s, goto partial; } - algn_diff = 0 - (uintptr_t)src & 0xF; + algn_diff = (0 - (uintptr_t)src) & 0xF; if (algn_diff) { xmm_crc_part = _mm_loadu_si128((__m128i *)src); _mm_storeu_si128((__m128i *)dst, xmm_crc_part); @@ -491,3 +493,5 @@ unsigned ZLIB_INTERNAL crc_fold_512to32(deflate_state *const s) return ~crc; CRC_SAVE(s) } + +#endif /* CRC32_SIMD_SSE42_PCLMUL */ @@ -60,6 +60,11 @@ #include "crc32_simd.h" #endif +#ifdef FASTEST +/* See http://crbug.com/1113596 */ +#error "FASTEST is not supported in Chromium's zlib." +#endif + const char deflate_copyright[] = " deflate 1.2.11 Copyright 1995-2017 Jean-loup Gailly and Mark Adler "; /* @@ -304,10 +309,9 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; - if (x86_cpu_enable_simd) { + s->hash_bits = memLevel + 7; + if ((x86_cpu_enable_simd || arm_cpu_enable_crc32) && s->hash_bits < 15) { s->hash_bits = 15; - } else { - s->hash_bits = memLevel + 7; } s->hash_size = 1 << s->hash_bits; @@ -317,6 +321,9 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->window = (Bytef *) ZALLOC(strm, s->w_size + window_padding, 2*sizeof(Byte)); + /* Avoid use of unitialized values in the window, see crbug.com/1137613 and + * crbug.com/1144420 */ + zmemzero(s->window, (s->w_size + window_padding) * (2 * sizeof(Byte))); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); /* Avoid use of uninitialized value, see: * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11360 @@ -1213,7 +1220,7 @@ ZLIB_INTERNAL unsigned deflate_read_buf(strm, buf, size) #ifdef GZIP if (strm->state->wrap == 2) copy_with_crc(strm, buf, len); - else + else #endif { zmemcpy(buf, strm->next_in, len); @@ -1346,7 +1353,16 @@ local uInt longest_match(s, cur_match) * necessary to put more guard bytes at the end of the window, or * to check more often for insufficient lookahead. */ - Assert(scan[2] == match[2], "scan[2]?"); + if (!x86_cpu_enable_simd && !arm_cpu_enable_crc32) { + Assert(scan[2] == match[2], "scan[2]?"); + } else { + /* When using CRC hashing, scan[2] and match[2] may mismatch, but in + * that case at least one of the other hashed bytes will mismatch + * also. Bytes 0 and 1 were already checked above, and we know there + * are at least four bytes to check otherwise the mismatch would have + * been found by the scan_end comparison above, so: */ + Assert(scan[2] == match[2] || scan[3] != match[3], "scan[2]??"); + } scan++, match++; do { } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && @@ -1377,7 +1393,16 @@ local uInt longest_match(s, cur_match) * the hash keys are equal and that HASH_BITS >= 8. */ scan += 2, match++; - Assert(*scan == *match, "match[2]?"); + if (!x86_cpu_enable_simd && !arm_cpu_enable_crc32) { + Assert(*scan == *match, "match[2]?"); + } else { + /* When using CRC hashing, scan[2] and match[2] may mismatch, but in + * that case at least one of the other hashed bytes will mismatch + * also. Bytes 0 and 1 were already checked above, and we know there + * are at least four bytes to check otherwise the mismatch would have + * been found by the scan_end comparison above, so: */ + Assert(*scan == *match || scan[1] != match[1], "match[2]??"); + } /* We check for insufficient lookahead only every 8th comparison; * the 256th check will be made at strstart+258. @@ -1521,7 +1546,7 @@ local void fill_window_c(deflate_state *s); local void fill_window(deflate_state *s) { -#ifdef ADLER32_SIMD_SSSE3 +#ifdef DEFLATE_FILL_WINDOW_SSE2 if (x86_cpu_enable_simd) { fill_window_sse(s); return; @@ -2038,7 +2063,13 @@ local block_state deflate_slow(s, flush) uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ - check_match(s, s->strstart-1, s->prev_match, s->prev_length); + if (s->prev_match == -1) { + /* The window has slid one byte past the previous match, + * so the first byte cannot be compared. */ + check_match(s, s->strstart, s->prev_match+1, s->prev_length-1); + } else { + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + } _tr_tally_dist(s, s->strstart -1 - s->prev_match, s->prev_length - MIN_MATCH, bflush); diff --git a/fill_window_sse.c b/fill_window_sse.c index ed1e5d1..a841c99 100644 --- a/fill_window_sse.c +++ b/fill_window_sse.c @@ -9,9 +9,10 @@ * For conditions of distribution and use, see copyright notice in zlib.h */ -#include <immintrin.h> #include "deflate.h" +#ifdef DEFLATE_FILL_WINDOW_SSE2 + #define UPDATE_HASH(s,h,i) \ {\ if (s->level < 6) { \ @@ -28,6 +29,8 @@ extern int deflate_read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#include <immintrin.h> + void fill_window_sse(deflate_state *s) { const __m128i xmm_wsize = _mm_set1_epi16(s->w_size); @@ -175,3 +178,5 @@ void fill_window_sse(deflate_state *s) Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, "not enough room for search"); } + +#endif /* DEFLATE_FILL_WINDOW_SSE2 */ diff --git a/google/BUILD.gn b/google/BUILD.gn index a628d2f..c29e892 100644 --- a/google/BUILD.gn +++ b/google/BUILD.gn @@ -28,10 +28,10 @@ if (build_with_chromium) { "compression_utils.h", ] deps = [ - ":compression_utils_portable", "//base", "//third_party/zlib", ] + public_deps = [ ":compression_utils_portable" ] } } @@ -42,5 +42,5 @@ static_library("compression_utils_portable") { "compression_utils_portable.cc", "compression_utils_portable.h", ] - deps = [ "//third_party/zlib" ] + public_deps = [ "//third_party/zlib" ] } diff --git a/google/DEPS b/google/DEPS index 144fbd1..03f2cb9 100644 --- a/google/DEPS +++ b/google/DEPS @@ -2,4 +2,5 @@ include_rules = [ '+base', '+build', '+testing', + "+third_party/zlib/zlib.h", ] diff --git a/google/OWNERS b/google/OWNERS index 1ca2531..868af3c 100644 --- a/google/OWNERS +++ b/google/OWNERS @@ -3,3 +3,4 @@ satorux@chromium.org # compression_utils* asvitkine@chromium.org isherman@chromium.org +cavalcantii@chromium.org diff --git a/google/compression_utils.cc b/google/compression_utils.cc index 9f63a84..781c805 100644 --- a/google/compression_utils.cc +++ b/google/compression_utils.cc @@ -5,16 +5,15 @@ #include "third_party/zlib/google/compression_utils.h" #include "base/bit_cast.h" -#include "base/logging.h" +#include "base/check_op.h" #include "base/process/memory.h" -#include "base/strings/string_piece.h" #include "base/sys_byteorder.h" #include "third_party/zlib/google/compression_utils_portable.h" namespace compression { -bool GzipCompress(base::StringPiece input, +bool GzipCompress(base::span<const char> input, char* output_buffer, size_t output_buffer_size, size_t* compressed_size, @@ -35,7 +34,11 @@ bool GzipCompress(base::StringPiece input, return true; } -bool GzipCompress(base::StringPiece input, std::string* output) { +bool GzipCompress(base::span<const char> input, std::string* output) { + return GzipCompress(base::as_bytes(input), output); +} + +bool GzipCompress(base::span<const uint8_t> input, std::string* output) { // Not using std::vector<> because allocation failures are recoverable, // which is hidden by std::vector<>. static_assert(sizeof(Bytef) == 1, ""); @@ -87,30 +90,44 @@ bool GzipUncompress(const std::string& input, std::string* output) { return false; } -bool GzipUncompress(base::StringPiece input, base::StringPiece output) { +bool GzipUncompress(base::span<const char> input, + base::span<const char> output) { + return GzipUncompress(base::as_bytes(input), base::as_bytes(output)); +} + +bool GzipUncompress(base::span<const uint8_t> input, + base::span<const uint8_t> output) { uLongf uncompressed_size = GetUncompressedSize(input); if (uncompressed_size > output.size()) return false; return zlib_internal::GzipUncompressHelper( bit_cast<Bytef*>(output.data()), &uncompressed_size, bit_cast<const Bytef*>(input.data()), - static_cast<uLongf>(input.length())) == Z_OK; + static_cast<uLongf>(input.size())) == Z_OK; } -bool GzipUncompress(base::StringPiece input, std::string* output) { +bool GzipUncompress(base::span<const char> input, std::string* output) { + return GzipUncompress(base::as_bytes(input), output); +} + +bool GzipUncompress(base::span<const uint8_t> input, std::string* output) { // Disallow in-place usage, i.e., |input| using |*output| as underlying data. - DCHECK_NE(input.data(), output->data()); + DCHECK_NE(reinterpret_cast<const char*>(input.data()), output->data()); uLongf uncompressed_size = GetUncompressedSize(input); output->resize(uncompressed_size); return zlib_internal::GzipUncompressHelper( bit_cast<Bytef*>(output->data()), &uncompressed_size, bit_cast<const Bytef*>(input.data()), - static_cast<uLongf>(input.length())) == Z_OK; + static_cast<uLongf>(input.size())) == Z_OK; +} + +uint32_t GetUncompressedSize(base::span<const char> compressed_data) { + return GetUncompressedSize(base::as_bytes(compressed_data)); } -uint32_t GetUncompressedSize(base::StringPiece compressed_data) { +uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data) { return zlib_internal::GetGzipUncompressedSize( - bit_cast<Bytef*>(compressed_data.data()), compressed_data.length()); + bit_cast<Bytef*>(compressed_data.data()), compressed_data.size()); } } // namespace compression diff --git a/google/compression_utils.h b/google/compression_utils.h index 5162207..cca47be 100644 --- a/google/compression_utils.h +++ b/google/compression_utils.h @@ -7,7 +7,7 @@ #include <string> -#include "base/strings/string_piece.h" +#include "base/containers/span.h" namespace compression { @@ -18,7 +18,7 @@ namespace compression { // |malloc_fn| and |free_fn| are pointers to malloc() and free()-like functions, // or nullptr to use the standard ones. // Returns true for success. -bool GzipCompress(base::StringPiece input, +bool GzipCompress(base::span<const char> input, char* output_buffer, size_t output_buffer_size, size_t* compressed_size, @@ -29,27 +29,41 @@ bool GzipCompress(base::StringPiece input, // |input| and |output| are allowed to point to the same string (in-place // operation). // Returns true for success. -bool GzipCompress(base::StringPiece input, std::string* output); +bool GzipCompress(base::span<const char> input, std::string* output); + +// Like the above method, but using uint8_t instead. +bool GzipCompress(base::span<const uint8_t> input, std::string* output); // Uncompresses the data in |input| using gzip, storing the result in |output|. // |input| and |output| are allowed to be the same string (in-place operation). // Returns true for success. bool GzipUncompress(const std::string& input, std::string* output); -// Like the above method, but uses base::StringPiece to avoid allocations if +// Like the above method, but uses base::span to avoid allocations if // needed. |output|'s size must be at least as large as the return value from // GetUncompressedSize. // Returns true for success. -bool GzipUncompress(base::StringPiece input, base::StringPiece output); +bool GzipUncompress(base::span<const char> input, + base::span<const char> output); + +// Like the above method, but using uint8_t instead. +bool GzipUncompress(base::span<const uint8_t> input, + base::span<const uint8_t> output); // Uncompresses the data in |input| using gzip, and writes the results to // |output|, which must NOT be the underlying string of |input|, and is resized // if necessary. // Returns true for success. -bool GzipUncompress(base::StringPiece input, std::string* output); +bool GzipUncompress(base::span<const char> input, std::string* output); + +// Like the above method, but using uint8_t instead. +bool GzipUncompress(base::span<const uint8_t> input, std::string* output); // Returns the uncompressed size from GZIP-compressed |compressed_data|. -uint32_t GetUncompressedSize(base::StringPiece compressed_data); +uint32_t GetUncompressedSize(base::span<const char> compressed_data); + +// Like the above method, but using uint8_t instead. +uint32_t GetUncompressedSize(base::span<const uint8_t> compressed_data); } // namespace compression diff --git a/google/compression_utils_portable.cc b/google/compression_utils_portable.cc index 191e349..331e41e 100644 --- a/google/compression_utils_portable.cc +++ b/google/compression_utils_portable.cc @@ -5,7 +5,7 @@ * found in the Chromium source repository LICENSE file. */ -#include "third_party/zlib/google/compression_utils_portable.h" +#include "compression_utils_portable.h" #include <stddef.h> #include <stdlib.h> @@ -84,7 +84,7 @@ int CompressHelper(WrapperType wrapper_type, int compression_level, void* (*malloc_fn)(size_t), void (*free_fn)(void*)) { - if (compression_level < 1 || compression_level > 9) { + if (compression_level < 0 || compression_level > 9) { compression_level = Z_DEFAULT_COMPRESSION; } diff --git a/google/compression_utils_portable.h b/google/compression_utils_portable.h index cd004e8..c1f3775 100644 --- a/google/compression_utils_portable.h +++ b/google/compression_utils_portable.h @@ -9,10 +9,14 @@ #include <stdint.h> +/* TODO(cavalcantii): remove support for Chromium ever building with a system + * zlib. + */ #if defined(USE_SYSTEM_ZLIB) #include <zlib.h> +/* AOSP build requires relative paths. */ #else -#include "third_party/zlib/zlib.h" +#include "zlib.h" #endif namespace zlib_internal { diff --git a/google/compression_utils_unittest.cc b/google/compression_utils_unittest.cc index b0e04b8..31c3226 100644 --- a/google/compression_utils_unittest.cc +++ b/google/compression_utils_unittest.cc @@ -9,7 +9,6 @@ #include <string> -#include "base/logging.h" #include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -55,13 +54,9 @@ TEST(CompressionUtilsTest, GzipUncompression) { EXPECT_EQ(golden_data, uncompressed_data); } -TEST(CompressionUtilsTest, GzipUncompressionFromStringPieceToString) { - base::StringPiece compressed_data( - reinterpret_cast<const char*>(kCompressedData), - base::size(kCompressedData)); - +TEST(CompressionUtilsTest, GzipUncompressionFromSpanToString) { std::string uncompressed_data; - EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data)); + EXPECT_TRUE(GzipUncompress(kCompressedData, &uncompressed_data)); std::string golden_data(reinterpret_cast<const char*>(kData), base::size(kData)); diff --git a/google/zip_internal.cc b/google/zip_internal.cc index 314740f..354fbf8 100644 --- a/google/zip_internal.cc +++ b/google/zip_internal.cc @@ -5,10 +5,12 @@ #include "third_party/zlib/google/zip_internal.h" #include <stddef.h> +#include <string.h> #include <algorithm> #include "base/logging.h" +#include "base/notreached.h" #include "base/strings/utf_string_conversions.h" #if defined(USE_SYSTEM_MINIZIP) @@ -54,10 +56,10 @@ void* ZipOpenFunc(void *opaque, const char* filename, int mode) { creation_disposition = CREATE_ALWAYS; } - base::string16 filename16 = base::UTF8ToUTF16(filename); + std::wstring filenamew = base::UTF8ToWide(filename); if ((filename != NULL) && (desired_access != 0)) { - file = CreateFile(filename16.c_str(), desired_access, share_mode, - NULL, creation_disposition, flags_and_attributes, NULL); + file = CreateFile(filenamew.c_str(), desired_access, share_mode, NULL, + creation_disposition, flags_and_attributes, NULL); } if (file == INVALID_HANDLE_VALUE) diff --git a/google/zip_reader.cc b/google/zip_reader.cc index 8b4bb4a..1910cf2 100644 --- a/google/zip_reader.cc +++ b/google/zip_reader.cc @@ -10,10 +10,9 @@ #include "base/files/file.h" #include "base/logging.h" #include "base/macros.h" -#include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "build/build_config.h" #include "third_party/zlib/google/zip_internal.h" @@ -102,7 +101,7 @@ ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip, is_unsafe_ = file_path_.ReferencesParent(); // We also consider that the file name is unsafe, if it's invalid UTF-8. - base::string16 file_name_utf16; + std::u16string file_name_utf16; if (!base::UTF8ToUTF16(file_name_in_zip.data(), file_name_in_zip.size(), &file_name_utf16)) { is_unsafe_ = true; @@ -296,11 +295,11 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( // If this is a directory, just create it and return. if (current_entry_info()->is_directory()) { if (base::CreateDirectory(output_file_path)) { - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(success_callback)); } else { DVLOG(1) << "Unzip failed: unable to create directory."; - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, std::move(failure_callback)); } return; @@ -308,16 +307,16 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( if (unzOpenCurrentFile(zip_file_) != UNZ_OK) { DVLOG(1) << "Unzip failed: unable to open current zip entry."; - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - std::move(failure_callback)); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(failure_callback)); return; } base::FilePath output_dir_path = output_file_path.DirName(); if (!base::CreateDirectory(output_dir_path)) { DVLOG(1) << "Unzip failed: unable to create containing directory."; - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - std::move(failure_callback)); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(failure_callback)); return; } @@ -327,17 +326,17 @@ void ZipReader::ExtractCurrentEntryToFilePathAsync( if (!output_file.IsValid()) { DVLOG(1) << "Unzip failed: unable to create platform file at " << output_file_path.value(); - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - std::move(failure_callback)); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(failure_callback)); return; } - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(), - Passed(std::move(output_file)), - std::move(success_callback), std::move(failure_callback), - progress_callback, 0 /* initial offset */)); + std::move(output_file), std::move(success_callback), + std::move(failure_callback), progress_callback, + 0 /* initial offset */)); } bool ZipReader::ExtractCurrentEntryToString(uint64_t max_read_bytes, @@ -434,12 +433,12 @@ void ZipReader::ExtractChunk(base::File output_file, progress_callback.Run(current_progress); - base::ThreadTaskRunnerHandle::Get()->PostTask( + base::SequencedTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&ZipReader::ExtractChunk, weak_ptr_factory_.GetWeakPtr(), - Passed(std::move(output_file)), - std::move(success_callback), std::move(failure_callback), - progress_callback, current_progress)); + std::move(output_file), std::move(success_callback), + std::move(failure_callback), progress_callback, + current_progress)); } } diff --git a/google/zip_reader_unittest.cc b/google/zip_reader_unittest.cc index 87190c7..44134f8 100644 --- a/google/zip_reader_unittest.cc +++ b/google/zip_reader_unittest.cc @@ -12,14 +12,15 @@ #include <string> #include "base/bind.h" +#include "base/check.h" #include "base/files/file.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/hash/md5.h" -#include "base/logging.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/stl_util.h" +#include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/task_environment.h" @@ -510,12 +511,12 @@ TEST_F(ZipReaderTest, ExtractCurrentEntryToString) { if (i > 0) { // Exact byte read limit: must pass. EXPECT_TRUE(reader.ExtractCurrentEntryToString(i, &contents)); - EXPECT_EQ(base::StringPiece("0123456", i).as_string(), contents); + EXPECT_EQ(std::string(base::StringPiece("0123456", i)), contents); } // More than necessary byte read limit: must pass. EXPECT_TRUE(reader.ExtractCurrentEntryToString(16, &contents)); - EXPECT_EQ(base::StringPiece("0123456", i).as_string(), contents); + EXPECT_EQ(std::string(base::StringPiece("0123456", i)), contents); } reader.Close(); } diff --git a/google/zip_unittest.cc b/google/zip_unittest.cc index 7ea3c36..10f2ef7 100644 --- a/google/zip_unittest.cc +++ b/google/zip_unittest.cc @@ -16,6 +16,7 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/logging.h" #include "base/macros.h" #include "base/path_service.h" #include "base/strings/string_util.h" diff --git a/google/zip_writer.cc b/google/zip_writer.cc index 1f2f073..6f38d42 100644 --- a/google/zip_writer.cc +++ b/google/zip_writer.cc @@ -5,6 +5,7 @@ #include "third_party/zlib/google/zip_writer.h" #include "base/files/file.h" +#include "base/logging.h" #include "base/strings/string_util.h" #include "third_party/zlib/google/zip_internal.h" diff --git a/libz.map.txt b/libz.map.txt index 0059aa7..850bbf8 100644 --- a/libz.map.txt +++ b/libz.map.txt @@ -79,7 +79,7 @@ ZLIB_1.2.5.2 { inflateResetKeep; } ZLIB_1.2.5.1; -ZLIB_1.2.7.1 { # introduced=18 +ZLIB_1.2.7.1 { # introduced=19 inflateGetDictionary; gzvprintf; } ZLIB_1.2.5.2; diff --git a/patches/0004-fix-uwp.patch b/patches/0004-fix-uwp.patch new file mode 100644 index 0000000..23145a7 --- /dev/null +++ b/patches/0004-fix-uwp.patch @@ -0,0 +1,22 @@ +diff --git a/third_party/zlib/contrib/minizip/iowin32.c b/third_party/zlib/contrib/minizip/iowin32.c +index 246ceb91a139..c6bc314b3c28 100644 +--- a/third_party/zlib/contrib/minizip/iowin32.c ++++ b/third_party/zlib/contrib/minizip/iowin32.c +@@ -31,14 +31,12 @@ + #define _WIN32_WINNT 0x601 + #endif + +-#if _WIN32_WINNT >= _WIN32_WINNT_WIN8 +-// see Include/shared/winapifamily.h in the Windows Kit +-#if defined(WINAPI_FAMILY_PARTITION) && (!(defined(IOWIN32_USING_WINRT_API))) +-#if WINAPI_FAMILY_ONE_PARTITION(WINAPI_FAMILY, WINAPI_PARTITION_APP) ++#if !defined(IOWIN32_USING_WINRT_API) ++#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) ++// Windows Store or Universal Windows Platform + #define IOWIN32_USING_WINRT_API 1 + #endif + #endif +-#endif + + voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); + uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); diff --git a/patches/0005-infcover-gtest.patch b/patches/0005-infcover-gtest.patch new file mode 100644 index 0000000..f5443bd --- /dev/null +++ b/patches/0005-infcover-gtest.patch @@ -0,0 +1,405 @@ +From 409594639f15d825202971db7a275023e05772ff Mon Sep 17 00:00:00 2001 +From: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com> +Date: Tue, 28 Apr 2020 10:48:01 -0700 +Subject: [PATCH] Local Changes: - make C tests build as C++ code so we can + use gtest. - use gtest EXPECT_TRUE instead of C assert. - replace C + streams for C++ (portability issues). + +--- + test/infcover.c | 167 ++++++++++++++++++++++++++---------------------- + 1 file changed, 90 insertions(+), 77 deletions(-) + +diff --git a/test/infcover.c b/test/infcover.c +index 2be0164..a8c51c7 100644 +--- a/test/infcover.c ++++ b/test/infcover.c +@@ -4,11 +4,12 @@ + */ + + /* to use, do: ./configure --cover && make cover */ +- ++// clang-format off ++#include "infcover.h" + #include <stdio.h> + #include <stdlib.h> + #include <string.h> +-#include <assert.h> ++ + #include "zlib.h" + + /* get definition of internal structure so we can mess with it (see pull()), +@@ -17,8 +18,22 @@ + #include "inftrees.h" + #include "inflate.h" + ++/* XXX: use C++ streams instead of printf/fputs/etc due to portability ++ * as type sizes can vary between platforms. ++ */ ++#include <iostream> + #define local static + ++/* XXX: hacking C assert and plugging into GTest. */ ++#include "gtest.h" ++#if defined(assert) ++#undef assert ++#define assert EXPECT_TRUE ++#endif ++ ++/* XXX: handle what is a reserved word in C++. */ ++#define try try_f ++ + /* -- memory tracking routines -- */ + + /* +@@ -72,7 +87,7 @@ local void *mem_alloc(void *mem, unsigned count, unsigned size) + { + void *ptr; + struct mem_item *item; +- struct mem_zone *zone = mem; ++ struct mem_zone *zone = static_cast<struct mem_zone *>(mem); + size_t len = count * (size_t)size; + + /* induced allocation failure */ +@@ -87,7 +102,7 @@ local void *mem_alloc(void *mem, unsigned count, unsigned size) + memset(ptr, 0xa5, len); + + /* create a new item for the list */ +- item = malloc(sizeof(struct mem_item)); ++ item = static_cast<struct mem_item *>(malloc(sizeof(struct mem_item))); + if (item == NULL) { + free(ptr); + return NULL; +@@ -112,7 +127,7 @@ local void *mem_alloc(void *mem, unsigned count, unsigned size) + local void mem_free(void *mem, void *ptr) + { + struct mem_item *item, *next; +- struct mem_zone *zone = mem; ++ struct mem_zone *zone = static_cast<struct mem_zone *>(mem); + + /* if no zone, just do a free */ + if (zone == NULL) { +@@ -159,7 +174,7 @@ local void mem_setup(z_stream *strm) + { + struct mem_zone *zone; + +- zone = malloc(sizeof(struct mem_zone)); ++ zone = static_cast<struct mem_zone *>(malloc(sizeof(struct mem_zone))); + assert(zone != NULL); + zone->first = NULL; + zone->total = 0; +@@ -175,33 +190,33 @@ local void mem_setup(z_stream *strm) + /* set a limit on the total memory allocation, or 0 to remove the limit */ + local void mem_limit(z_stream *strm, size_t limit) + { +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + + zone->limit = limit; + } + + /* show the current total requested allocations in bytes */ +-local void mem_used(z_stream *strm, char *prefix) ++local void mem_used(z_stream *strm, const char *prefix) + { +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + +- fprintf(stderr, "%s: %lu allocated\n", prefix, zone->total); ++ std::cout << prefix << ": " << zone->total << " allocated" << std::endl; + } + + /* show the high water allocation in bytes */ +-local void mem_high(z_stream *strm, char *prefix) ++local void mem_high(z_stream *strm, const char *prefix) + { +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + +- fprintf(stderr, "%s: %lu high water mark\n", prefix, zone->highwater); ++ std::cout << prefix << ": " << zone->highwater << " high water mark" << std::endl; + } + + /* release the memory allocation zone -- if there are any surprises, notify */ +-local void mem_done(z_stream *strm, char *prefix) ++local void mem_done(z_stream *strm, const char *prefix) + { + int count = 0; + struct mem_item *item, *next; +- struct mem_zone *zone = strm->opaque; ++ struct mem_zone *zone = static_cast<struct mem_zone *>(strm->opaque); + + /* show high water mark */ + mem_high(strm, prefix); +@@ -218,13 +233,20 @@ local void mem_done(z_stream *strm, char *prefix) + + /* issue alerts about anything unexpected */ + if (count || zone->total) +- fprintf(stderr, "** %s: %lu bytes in %d blocks not freed\n", +- prefix, zone->total, count); ++ std::cout << "** " << prefix << ": " ++ << zone->total << " bytes in " ++ << count << " blocks not freed" ++ << std::endl; ++ + if (zone->notlifo) +- fprintf(stderr, "** %s: %d frees not LIFO\n", prefix, zone->notlifo); ++ std::cout << "** " << prefix << ": " ++ << zone->notlifo << " frees not LIFO" ++ << std::endl; ++ + if (zone->rogue) +- fprintf(stderr, "** %s: %d frees not recognized\n", +- prefix, zone->rogue); ++ std::cout << "** " << prefix << ": " ++ << zone->rogue << " frees not recognized" ++ << std::endl; + + /* free the zone and delete from the stream */ + free(zone); +@@ -247,7 +269,7 @@ local unsigned char *h2b(const char *hex, unsigned *len) + unsigned char *in, *re; + unsigned next, val; + +- in = malloc((strlen(hex) + 1) >> 1); ++ in = static_cast<unsigned char *>(malloc((strlen(hex) + 1) >> 1)); + if (in == NULL) + return NULL; + next = 0; +@@ -268,7 +290,7 @@ local unsigned char *h2b(const char *hex, unsigned *len) + } while (*hex++); /* go through the loop with the terminating null */ + if (len != NULL) + *len = next; +- re = realloc(in, next); ++ re = static_cast<unsigned char *>(realloc(in, next)); + return re == NULL ? in : re; + } + +@@ -281,7 +303,7 @@ local unsigned char *h2b(const char *hex, unsigned *len) + header information is collected with inflateGetHeader(). If a zlib stream + is looking for a dictionary, then an empty dictionary is provided. + inflate() is run until all of the input data is consumed. */ +-local void inf(char *hex, char *what, unsigned step, int win, unsigned len, ++local void inf(const char *hex, const char *what, unsigned step, int win, unsigned len, + int err) + { + int ret; +@@ -298,7 +320,7 @@ local void inf(char *hex, char *what, unsigned step, int win, unsigned len, + mem_done(&strm, what); + return; + } +- out = malloc(len); assert(out != NULL); ++ out = static_cast<unsigned char *>(malloc(len)); assert(out != NULL); + if (win == 47) { + head.extra = out; + head.extra_max = len; +@@ -347,7 +369,7 @@ local void inf(char *hex, char *what, unsigned step, int win, unsigned len, + } + + /* cover all of the lines in inflate.c up to inflate() */ +-local void cover_support(void) ++void cover_support(void) + { + int ret; + z_stream strm; +@@ -381,11 +403,11 @@ local void cover_support(void) + strm.next_in = Z_NULL; + ret = inflateInit(&strm); assert(ret == Z_OK); + ret = inflateEnd(&strm); assert(ret == Z_OK); +- fputs("inflate built-in memory routines\n", stderr); ++ std::cout << "inflate built-in memory routines" << std::endl;; + } + + /* cover all inflate() header and trailer cases and code after inflate() */ +-local void cover_wrap(void) ++void cover_wrap(void) + { + int ret; + z_stream strm, copy; +@@ -394,7 +416,7 @@ local void cover_wrap(void) + ret = inflate(Z_NULL, 0); assert(ret == Z_STREAM_ERROR); + ret = inflateEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); + ret = inflateCopy(Z_NULL, Z_NULL); assert(ret == Z_STREAM_ERROR); +- fputs("inflate bad parameters\n", stderr); ++ std::cout << "inflate bad parameters" << std::endl; + + inf("1f 8b 0 0", "bad gzip method", 0, 31, 0, Z_DATA_ERROR); + inf("1f 8b 8 80", "bad gzip flags", 0, 31, 0, Z_DATA_ERROR); +@@ -415,9 +437,9 @@ local void cover_wrap(void) + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, -8); + strm.avail_in = 2; +- strm.next_in = (void *)"\x63"; ++ strm.next_in = (Bytef *)"\x63"; + strm.avail_out = 1; +- strm.next_out = (void *)&ret; ++ strm.next_out = (Bytef *)&ret; + mem_limit(&strm, 1); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_MEM_ERROR); +@@ -428,11 +450,11 @@ local void cover_wrap(void) + mem_limit(&strm, (sizeof(struct inflate_state) << 1) + 256); + ret = inflatePrime(&strm, 16, 0); assert(ret == Z_OK); + strm.avail_in = 2; +- strm.next_in = (void *)"\x80"; ++ strm.next_in = (Bytef *)"\x80"; + ret = inflateSync(&strm); assert(ret == Z_DATA_ERROR); + ret = inflate(&strm, Z_NO_FLUSH); assert(ret == Z_STREAM_ERROR); + strm.avail_in = 4; +- strm.next_in = (void *)"\0\0\xff\xff"; ++ strm.next_in = (Bytef *)"\0\0\xff\xff"; + ret = inflateSync(&strm); assert(ret == Z_OK); + (void)inflateSyncPoint(&strm); + ret = inflateCopy(©, &strm); assert(ret == Z_MEM_ERROR); +@@ -454,7 +476,7 @@ local unsigned pull(void *desc, unsigned char **buf) + next = 0; + return 0; /* no input (already provided at next_in) */ + } +- state = (void *)((z_stream *)desc)->state; ++ state = reinterpret_cast<struct inflate_state *>(((z_stream *)desc)->state); + if (state != Z_NULL) + state->mode = SYNC; /* force an otherwise impossible situation */ + return next < sizeof(dat) ? (*buf = dat + next++, 1) : 0; +@@ -467,7 +489,7 @@ local int push(void *desc, unsigned char *buf, unsigned len) + } + + /* cover inflateBack() up to common deflate data cases and after those */ +-local void cover_back(void) ++void cover_back(void) + { + int ret; + z_stream strm; +@@ -479,17 +501,17 @@ local void cover_back(void) + ret = inflateBack(Z_NULL, Z_NULL, Z_NULL, Z_NULL, Z_NULL); + assert(ret == Z_STREAM_ERROR); + ret = inflateBackEnd(Z_NULL); assert(ret == Z_STREAM_ERROR); +- fputs("inflateBack bad parameters\n", stderr); ++ std::cout << "inflateBack bad parameters" << std::endl;; + + mem_setup(&strm); + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + strm.avail_in = 2; +- strm.next_in = (void *)"\x03"; ++ strm.next_in = (Bytef *)"\x03"; + ret = inflateBack(&strm, pull, Z_NULL, push, Z_NULL); + assert(ret == Z_STREAM_END); + /* force output error */ + strm.avail_in = 3; +- strm.next_in = (void *)"\x63\x00"; ++ strm.next_in = (Bytef *)"\x63\x00"; + ret = inflateBack(&strm, pull, Z_NULL, push, &strm); + assert(ret == Z_BUF_ERROR); + /* force mode error by mucking with state */ +@@ -500,11 +522,11 @@ local void cover_back(void) + + ret = inflateBackInit(&strm, 15, win); assert(ret == Z_OK); + ret = inflateBackEnd(&strm); assert(ret == Z_OK); +- fputs("inflateBack built-in memory routines\n", stderr); ++ std::cout << "inflateBack built-in memory routines" << std::endl;; + } + + /* do a raw inflate of data in hexadecimal with both inflate and inflateBack */ +-local int try(char *hex, char *id, int err) ++local int try(const char *hex, const char *id, int err) + { + int ret; + unsigned len, size; +@@ -518,11 +540,11 @@ local int try(char *hex, char *id, int err) + + /* allocate work areas */ + size = len << 3; +- out = malloc(size); ++ out = static_cast<unsigned char *>(malloc(size)); + assert(out != NULL); +- win = malloc(32768); ++ win = static_cast<unsigned char *>(malloc(32768)); + assert(win != NULL); +- prefix = malloc(strlen(id) + 6); ++ prefix = static_cast<char *>(malloc(strlen(id) + 6)); + assert(prefix != NULL); + + /* first with inflate */ +@@ -578,7 +600,7 @@ local int try(char *hex, char *id, int err) + } + + /* cover deflate data cases in both inflate() and inflateBack() */ +-local void cover_inflate(void) ++void cover_inflate(void) + { + try("0 0 0 0 0", "invalid stored block lengths", 1); + try("3 0", "fixed", 0); +@@ -613,32 +635,33 @@ local void cover_inflate(void) + inf("63 18 5 40 c 0", "window wrap", 3, -8, 300, Z_OK); + } + ++/* XXX(cavalcantii): fix linking error due inflate_table. */ + /* cover remaining lines in inftrees.c */ +-local void cover_trees(void) +-{ +- int ret; +- unsigned bits; +- unsigned short lens[16], work[16]; +- code *next, table[ENOUGH_DISTS]; +- +- /* we need to call inflate_table() directly in order to manifest not- +- enough errors, since zlib insures that enough is always enough */ +- for (bits = 0; bits < 15; bits++) +- lens[bits] = (unsigned short)(bits + 1); +- lens[15] = 15; +- next = table; +- bits = 15; +- ret = inflate_table(DISTS, lens, 16, &next, &bits, work); +- assert(ret == 1); +- next = table; +- bits = 1; +- ret = inflate_table(DISTS, lens, 16, &next, &bits, work); +- assert(ret == 1); +- fputs("inflate_table not enough errors\n", stderr); +-} ++/* void cover_trees(void) */ ++/* { */ ++/* int ret; */ ++/* unsigned bits; */ ++/* unsigned short lens[16], work[16]; */ ++/* code *next, table[ENOUGH_DISTS]; */ ++ ++/* /\* we need to call inflate_table() directly in order to manifest not- */ ++/* enough errors, since zlib insures that enough is always enough *\/ */ ++/* for (bits = 0; bits < 15; bits++) */ ++/* lens[bits] = (unsigned short)(bits + 1); */ ++/* lens[15] = 15; */ ++/* next = table; */ ++/* bits = 15; */ ++/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ ++/* assert(ret == 1); */ ++/* next = table; */ ++/* bits = 1; */ ++/* ret = inflate_table(DISTS, lens, 16, &next, &bits, work); */ ++/* assert(ret == 1); */ ++/* fputs("inflate_table not enough errors\n", stderr); */ ++/* } */ + + /* cover remaining inffast.c decoding and window copying */ +-local void cover_fast(void) ++void cover_fast(void) + { + inf("e5 e0 81 ad 6d cb b2 2c c9 01 1e 59 63 ae 7d ee fb 4d fd b5 35 41 68" + " ff 7f 0f 0 0 0", "fast length extra bits", 0, -8, 258, Z_DATA_ERROR); +@@ -658,14 +681,4 @@ local void cover_fast(void) + Z_STREAM_END); + } + +-int main(void) +-{ +- fprintf(stderr, "%s\n", zlibVersion()); +- cover_support(); +- cover_wrap(); +- cover_back(); +- cover_inflate(); +- cover_trees(); +- cover_fast(); +- return 0; +-} ++// clang-format on +-- +2.21.1 (Apple Git-122.3) + diff --git a/patches/0006-fix-check_match.patch b/patches/0006-fix-check_match.patch new file mode 100644 index 0000000..b21c363 --- /dev/null +++ b/patches/0006-fix-check_match.patch @@ -0,0 +1,42 @@ +From 8304bdda5293ffd5b3efce8e4f54904b387029d6 Mon Sep 17 00:00:00 2001 +From: Hans Wennborg <hans@chromium.org> +Date: Wed, 23 Sep 2020 16:36:38 +0200 +Subject: [PATCH] Avoid crashing in check_match when prev_match == -1 + +prev_match can be set to -1 after sliding the window. In that case, the +window has slid past the first byte of the last match, which means it +cannot be compared in check_match. + +This would cause zlib to crash on some inputs to deflate when built +with ZLIB_DEBUG enabled. + +Check for this situation and avoid crashing by not trying to compare +the first byte. + +Bug: 1113142 +--- + third_party/zlib/deflate.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c +index cfdd2f46b230..d70732ec6fc2 100644 +--- a/third_party/zlib/deflate.c ++++ b/third_party/zlib/deflate.c +@@ -2060,7 +2060,13 @@ local block_state deflate_slow(s, flush) + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + +- check_match(s, s->strstart-1, s->prev_match, s->prev_length); ++ if (s->prev_match == -1) { ++ /* The window has slid one byte past the previous match, ++ * so the first byte cannot be compared. */ ++ check_match(s, s->strstart, s->prev_match+1, s->prev_length-1); ++ } else { ++ check_match(s, s->strstart-1, s->prev_match, s->prev_length); ++ } + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); +-- +2.28.0.681.g6f77f65b4e-goog + diff --git a/patches/0007-zero-init-deflate-window.patch b/patches/0007-zero-init-deflate-window.patch new file mode 100644 index 0000000..9dbbf53 --- /dev/null +++ b/patches/0007-zero-init-deflate-window.patch @@ -0,0 +1,40 @@ +From 92537ee19784e0e545f06d89b7d89ab532a18cff Mon Sep 17 00:00:00 2001 +From: Hans Wennborg <hans@chromium.org> +Date: Tue, 3 Nov 2020 15:54:09 +0100 +Subject: [PATCH] [zlib] Zero-initialize the window used for deflation + +Otherwise MSan complains about use-of-uninitialized values in the +window. +This happens in both regular deflate's longest_match and deflate_rle. + +Before crrev.com/822755 we used to suppress those reports, but it seems +better to fix it properly. That will also allow us to catch other +potential issues with MSan in these functions. + +The instances of this that we've seen only reproduce with +fill_window_sse(), not with the regular fill_window() function. Since +the former doesn't exist in upstream zlib, I'm not planning to send this +patch upstream. + +Bug: 1137613, 1144420 +--- + third_party/zlib/deflate.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c +index 8bf93e524875..fc7ae45905ff 100644 +--- a/third_party/zlib/deflate.c ++++ b/third_party/zlib/deflate.c +@@ -321,6 +321,9 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + s->window = (Bytef *) ZALLOC(strm, + s->w_size + window_padding, + 2*sizeof(Byte)); ++ /* Avoid use of unitialized values in the window, see crbug.com/1137613 and ++ * crbug.com/1144420 */ ++ zmemzero(s->window, (s->w_size + window_padding) * (2 * sizeof(Byte))); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + /* Avoid use of uninitialized value, see: + * https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11360 +-- +2.29.1.341.ge80a0c044ae-goog + @@ -45,6 +45,49 @@ extern "C" { #define ZLIB_VER_SUBREVISION 0 /* + * In Android's NDK we have one zlib.h for all the versions. + * zlib users tend to use ZLIB_VERNUM to check API availability, + * so we need to translate __ANDROID_API__ appropriately. + * + * ZLIB_1.2.7.1 and ZLIB_1.2.9 are the only API changes in the NDK's + * supported range of API levels. + * + * jb-mr2-dev (18): 1.2.7 (but not 1.2.7.1, where the APIs were added!) + * https://android.googlesource.com/platform/external/zlib/+/refs/heads/jb-mr2-dev/src/zlib.h + * kitkat-dev (19): 1.2.8 + * https://android.googlesource.com/platform/external/zlib/+/refs/heads/kitkat-dev/src/zlib.h + * + * oreo-mr1-dev (27): 1.2.8 + * https://android.googlesource.com/platform/external/zlib/+/refs/heads/oreo-mr1-dev/src/zlib.h + * pie-dev (28): 1.2.11 + * https://android.googlesource.com/platform/external/zlib/+/refs/heads/pie-dev/src/zlib.h + * + * So: + * >= 28 --> 1.2.11 + * >= 19 --> 1.2.8 + * < 19 --> 1.2.7 + */ +#if defined(__ANDROID__) +# if __ANDROID_API__ >= 28 + /* Already okay. */ +# elif __ANDROID_API__ >= 19 +# undef ZLIB_VERSION +# define ZLIB_VERSION "1.2.8" +# undef ZLIB_VERNUM +# define ZLIB_VERNUM 0x1280 +# undef ZLIB_VER_REVISION +# define ZLIB_VER_REVISION 8 +# else +# undef ZLIB_VERSION +# define ZLIB_VERSION "1.2.6" +# undef ZLIB_VERNUM +# define ZLIB_VERNUM 0x1260 +# undef ZLIB_VER_REVISION +# define ZLIB_VER_REVISION 6 +# endif +#endif + +/* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) @@ -652,9 +695,11 @@ ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, not perform any compression: this will be done by deflate(). */ +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); +#endif /* Returns the sliding dictionary being maintained by deflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -904,9 +949,11 @@ ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, inflate(). */ +#if !defined(__ANDROID__) || __ANDROID_API__ >= 19 ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, Bytef *dictionary, uInt *dictLength)); +#endif /* Returns the sliding dictionary being maintained by inflate. dictLength is set to the number of bytes in the dictionary, and that many bytes are copied @@ -1280,8 +1327,10 @@ ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, buffer with the uncompressed data up to that point. */ +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, const Bytef *source, uLong *sourceLen)); +#endif /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of @@ -1417,8 +1466,10 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); Z_STREAM_ERROR. */ +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, gzFile file)); +#endif /* Read up to nitems items of size size from file to buf, otherwise operating as gzread() does. This duplicates the interface of stdio's fread(), with @@ -1451,8 +1502,10 @@ ZEXTERN int ZEXPORT gzwrite OF((gzFile file, error. */ +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, z_size_t nitems, gzFile file)); +#endif /* gzfwrite() writes nitems items of size size from buf to file, duplicating the interface of stdio's fwrite(), with size_t request and return types. If @@ -1704,8 +1757,10 @@ ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); if (adler != original_adler) error(); */ +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, z_size_t len)); +#endif /* Same as adler32(), but with a size_t length. */ @@ -1739,8 +1794,10 @@ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); if (crc != original_crc) error(); */ +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, z_size_t len)); +#endif /* Same as crc32(), but with a size_t length. */ @@ -1912,8 +1969,12 @@ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +#endif +#if !defined(__ANDROID__) || __ANDROID_API__ >= 28 ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +#endif ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) @@ -1922,9 +1983,11 @@ ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, #endif #if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifndef Z_SOLO +# if !defined(__ANDROID__) || __ANDROID_API__ >= 19 ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, const char *format, va_list va)); +# endif # endif #endif |