diff --git a/deps/zlib/BUILD.gn b/deps/zlib/BUILD.gn index 9f3938021f7a44..afd3e8cc0f38e9 100644 --- a/deps/zlib/BUILD.gn +++ b/deps/zlib/BUILD.gn @@ -419,7 +419,7 @@ static_library("minizip") { ] } - if (is_apple || is_android || is_nacl) { + if (is_apple || is_android) { # 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" ] @@ -551,6 +551,7 @@ if (build_with_chromium) { "google:compression_utils", "google:zip", "//base/test:test_support", + "//crypto", "//testing/gtest", ] diff --git a/deps/zlib/README.chromium b/deps/zlib/README.chromium index 1f7c7460451113..b98a65addb10e6 100644 --- a/deps/zlib/README.chromium +++ b/deps/zlib/README.chromium @@ -1,8 +1,9 @@ Name: zlib Short Name: zlib -URL: http://zlib.net/ +URL: https://github.com/madler/zlib Version: 1.3.1 Revision: 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf +Update Mechanism: Manual (https://crbug.com/422348588) CPEPrefix: cpe:/a:zlib:zlib:1.3.1 Security Critical: yes Shipped: yes @@ -19,8 +20,8 @@ library. zlib implements the "deflate" compression algorithm described by RFC also implements the zlib (RFC 1950) and gzip (RFC 1952) wrapper formats. Local Modifications: - - Only source code from the zlib distribution used to build the zlib and - minizip libraries are present. Many other files have been omitted. Only *.c + - Only source code from the http://zlib.net distribution used to build the zlib + and minizip libraries are present. Many other files have been omitted. Only *.c and *.h files from the upstream root directory, contrib/minizip and examples/zpipe.c were imported. - The files named '*simd*' are original x86/Arm/RISC-V specific optimizations. diff --git a/deps/zlib/adler32_simd.c b/deps/zlib/adler32_simd.c index b3e1f0a3dfda01..08c21d3e953ac6 100644 --- a/deps/zlib/adler32_simd.c +++ b/deps/zlib/adler32_simd.c @@ -53,6 +53,9 @@ #include +#if defined(__GNUC__) +__attribute__((__target__("ssse3"))) +#endif uint32_t ZLIB_INTERNAL adler32_simd_( /* SSSE3 */ uint32_t adler, const unsigned char *buf, diff --git a/deps/zlib/contrib/minizip/README.chromium b/deps/zlib/contrib/minizip/README.chromium index ee70ec59ad1bf2..0299a7e331f9ee 100644 --- a/deps/zlib/contrib/minizip/README.chromium +++ b/deps/zlib/contrib/minizip/README.chromium @@ -3,6 +3,7 @@ Short Name: minizip URL: https://github.com/madler/zlib/tree/master/contrib/minizip Version: 1.3.1.1 Revision: ef24c4c7502169f016dcd2a26923dbaf3216748c +Update Mechanism: Manual License: Zlib License File: //third_party/zlib/LICENSE Shipped: yes diff --git a/deps/zlib/contrib/minizip/unzip.c b/deps/zlib/contrib/minizip/unzip.c index 95a945c0ac5fe9..b77b787ccc0dbe 100644 --- a/deps/zlib/contrib/minizip/unzip.c +++ b/deps/zlib/contrib/minizip/unzip.c @@ -64,6 +64,7 @@ */ +#include #include #include #include @@ -837,6 +838,7 @@ local int unz64local_GetCurrentFileInfoInternal(unzFile file, uLong uMagic; long lSeek=0; uLong uL; + uLong uFileNameCrc; if (file==NULL) return UNZ_PARAMERROR; @@ -908,21 +910,34 @@ local int unz64local_GetCurrentFileInfoInternal(unzFile file, file_info_internal.offset_curfile = uL; lSeek+=file_info.size_filename; - if ((err==UNZ_OK) && (szFileName!=NULL)) + if (err==UNZ_OK) { - uLong uSizeRead ; - if (file_info.size_filename 0) { - *(szFileName+file_info.size_filename)='\0'; - uSizeRead = file_info.size_filename; + if (ZREAD64(s->z_filefunc, s->filestream, szCurrentFileName, file_info.size_filename) != file_info.size_filename) + { + err=UNZ_ERRNO; + } } - else - uSizeRead = fileNameBufferSize; - if ((file_info.size_filename>0) && (fileNameBufferSize>0)) - if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) - err=UNZ_ERRNO; - lSeek -= uSizeRead; + uFileNameCrc = crc32(0, (unsigned char*)szCurrentFileName, file_info.size_filename); + + if (szFileName != NULL) + { + if (fileNameBufferSize <= file_info.size_filename) + { + memcpy(szFileName, szCurrentFileName, fileNameBufferSize); + } + else + { + memcpy(szFileName, szCurrentFileName, file_info.size_filename); + szFileName[file_info.size_filename] = '\0'; + } + } + + lSeek -= file_info.size_filename; } // Read extrafield @@ -1012,7 +1027,15 @@ local int unz64local_GetCurrentFileInfoInternal(unzFile file, { int version = 0; - if (unz64local_getByte(&s->z_filefunc, s->filestream, &version) != UNZ_OK) + if (dataSize < 1 + 4) + { + /* dataSize includes version (1 byte), uCrc (4 bytes), and + * the filename data. If it's too small, fileNameSize below + * would overflow. */ + err = UNZ_ERRNO; + break; + } + else if (unz64local_getByte(&s->z_filefunc, s->filestream, &version) != UNZ_OK) { err = UNZ_ERRNO; } @@ -1025,16 +1048,16 @@ local int unz64local_GetCurrentFileInfoInternal(unzFile file, } else { - uLong uCrc, uHeaderCrc, fileNameSize; + uLong uCrc, fileNameSize; if (unz64local_getLong(&s->z_filefunc, s->filestream, &uCrc) != UNZ_OK) { err = UNZ_ERRNO; } - uHeaderCrc = crc32(0, (const unsigned char *)szFileName, file_info.size_filename); - fileNameSize = dataSize - (2 * sizeof (short) + 1); + fileNameSize = dataSize - (1 + 4); /* 1 for version, 4 for uCrc */ + /* Check CRC against file name in the header. */ - if (uHeaderCrc != uCrc) + if (uCrc != uFileNameCrc) { if (ZSEEK64(s->z_filefunc, s->filestream, fileNameSize, ZLIB_FILEFUNC_SEEK_CUR) != 0) { @@ -1043,24 +1066,28 @@ local int unz64local_GetCurrentFileInfoInternal(unzFile file, } else { - uLong uSizeRead; - file_info.size_filename = fileNameSize; - if (fileNameSize < fileNameBufferSize) - { - *(szFileName + fileNameSize) = '\0'; - uSizeRead = fileNameSize; - } - else + char szCurrentFileName[UINT16_MAX] = {0}; + + if (file_info.size_filename > 0) { - uSizeRead = fileNameBufferSize; + if (ZREAD64(s->z_filefunc, s->filestream, szCurrentFileName, file_info.size_filename) != file_info.size_filename) + { + err = UNZ_ERRNO; + } } - if ((fileNameSize > 0) && (fileNameBufferSize > 0)) + + if (szFileName != NULL) { - if (ZREAD64(s->z_filefunc, s->filestream, szFileName, uSizeRead) != uSizeRead) + if (fileNameBufferSize <= file_info.size_filename) { - err = UNZ_ERRNO; + memcpy(szFileName, szCurrentFileName, fileNameBufferSize); + } + else + { + memcpy(szFileName, szCurrentFileName, file_info.size_filename); + szFileName[file_info.size_filename] = '\0'; } } } diff --git a/deps/zlib/contrib/tests/fuzzers/minizip_unzip_fuzzer.cc b/deps/zlib/contrib/tests/fuzzers/minizip_unzip_fuzzer.cc index 3b9fa446bfba60..fbb779076e5858 100644 --- a/deps/zlib/contrib/tests/fuzzers/minizip_unzip_fuzzer.cc +++ b/deps/zlib/contrib/tests/fuzzers/minizip_unzip_fuzzer.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include #include +#include #include #include "unzip.h" @@ -19,11 +21,30 @@ } while (0) extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + + unsigned long filename_sz = fdp.ConsumeIntegralInRange(0, UINT16_MAX + 3); + unsigned long extra_sz = fdp.ConsumeIntegralInRange(0, UINT16_MAX + 3); + unsigned long comment_sz = fdp.ConsumeIntegralInRange(0, UINT16_MAX + 3); + + std::unique_ptr filename; + if (fdp.ConsumeBool()) { + filename = std::make_unique(filename_sz); + } + std::unique_ptr extra; + if (fdp.ConsumeBool()) { + extra = std::make_unique(extra_sz); + } + std::unique_ptr comment; + if (fdp.ConsumeBool()) { + comment = std::make_unique(comment_sz); + } + // Mock read-only filesystem with only one file, file_data. In the calls // below, 'opaque' points to file_data, and 'strm' points to the file's seek // position, which is heap allocated so that failing to "close" it triggers a // leak error. - std::vector file_data(data, data + size); + std::vector file_data = fdp.ConsumeRemainingBytes(); zlib_filefunc64_def file_func = { .zopen64_file = [](void* opaque, const void* filename, int mode) -> void* { @@ -83,19 +104,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { while (true) { unz_file_info64 info = {0}; - // TODO: Pass nullptrs and different buffer sizes to cover more code. - char filename[UINT16_MAX + 1]; // +1 for the null terminator. - char extra[UINT16_MAX]; // No null terminator. - char comment[UINT16_MAX + 1]; // +1 for the null terminator. - - if (unzGetCurrentFileInfo64(uzf, &info, filename, sizeof(filename), extra, - sizeof(extra), comment, sizeof(comment)) == UNZ_OK) { - ASSERT(info.size_filename <= UINT16_MAX); - ASSERT(info.size_file_extra <= UINT16_MAX); - ASSERT(info.size_file_comment <= UINT16_MAX); - - ASSERT(filename[info.size_filename] == '\0'); - ASSERT(comment[info.size_file_comment] == '\0'); + if (unzGetCurrentFileInfo64(uzf, &info, filename.get(), filename_sz, extra.get(), + extra_sz, comment.get(), comment_sz) == UNZ_OK) { + if (filename) { + ASSERT(info.size_filename <= UINT16_MAX); + if (info.size_filename < filename_sz) { + ASSERT(filename[info.size_filename] == '\0'); + } + } + if (extra) { + ASSERT(info.size_file_extra <= UINT16_MAX); + } + if (comment) { + ASSERT(info.size_file_comment <= UINT16_MAX); + if (info.size_file_comment < comment_sz) { + ASSERT(comment[info.size_file_comment] == '\0'); + } + } } if (unzOpenCurrentFile(uzf) == UNZ_OK) { diff --git a/deps/zlib/contrib/tests/utils_unittest.cc b/deps/zlib/contrib/tests/utils_unittest.cc index f487a06996c98b..7d3772aa05df36 100644 --- a/deps/zlib/contrib/tests/utils_unittest.cc +++ b/deps/zlib/contrib/tests/utils_unittest.cc @@ -13,6 +13,7 @@ #if !defined(CMAKE_STANDALONE_UNITTESTS) #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" +#include "base/path_service.h" #include "third_party/zlib/contrib/minizip/unzip.h" #include "third_party/zlib/contrib/minizip/zip.h" @@ -1287,4 +1288,62 @@ TEST(ZlibTest, ZipExtraFieldSize) { EXPECT_EQ(unzClose(uzf), UNZ_OK); } +static base::FilePath TestDataDir() { + base::FilePath path; + bool success = base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path); + EXPECT_TRUE(success); + return path + .AppendASCII("third_party") + .AppendASCII("zlib") + .AppendASCII("google") + .AppendASCII("test") + .AppendASCII("data"); +} + +TEST(ZlibTest, ZipUnicodePathExtraSizeFilenameOverflow) { + // This is based on components/test/data/unzip_service/bug953599.zip (added + // in https://crrev.com/1004132), with the Unicode Path Extra Field's + // dataSize hex edited to four. + base::FilePath zip_file = TestDataDir().AppendASCII("unicode_path_extra_overflow.zip"); + unzFile uzf = unzOpen(zip_file.AsUTF8Unsafe().c_str()); + ASSERT_NE(uzf, nullptr); + EXPECT_EQ(unzGoToFirstFile(uzf), UNZ_ERRNO); + EXPECT_EQ(unzClose(uzf), UNZ_OK); +} + +TEST(ZlibTest, ZipUnicodePathExtra) { + // This is components/test/data/unzip_service/bug953599.zip (added in + // https://crrev.com/1004132). + base::FilePath zip_file = TestDataDir().AppendASCII("unicode_path_extra.zip"); + unzFile uzf = unzOpen(zip_file.AsUTF8Unsafe().c_str()); + ASSERT_NE(uzf, nullptr); + + char long_buf[15], short_buf[3]; + unz_file_info file_info; + + ASSERT_EQ(unzGoToFirstFile(uzf), UNZ_OK); + ASSERT_EQ(unzGetCurrentFileInfo(uzf, &file_info, long_buf, sizeof(long_buf), + nullptr, 0, nullptr, 0), UNZ_OK); + ASSERT_EQ(file_info.size_filename, 14); + ASSERT_EQ(std::string(long_buf), "\xec\x83\x88 \xeb\xac\xb8\xec\x84\x9c.txt"); + + // Even if the file name buffer is too short to hold the whole filename, the + // unicode path extra field should get parsed correctly, size_filename set, + // and the file name buffer should receive the first bytes. + ASSERT_EQ(unzGoToFirstFile(uzf), UNZ_OK); + ASSERT_EQ(unzGetCurrentFileInfo(uzf, &file_info, short_buf, sizeof(short_buf), + nullptr, 0, nullptr, 0), UNZ_OK); + ASSERT_EQ(file_info.size_filename, 14); + ASSERT_EQ(std::string(short_buf, sizeof(short_buf)), "\xec\x83\x88"); + + // Also with a null filename buffer, the unicode path extra field should get + // parsed and size_filename set correctly. + ASSERT_EQ(unzGoToFirstFile(uzf), UNZ_OK); + ASSERT_EQ(unzGetCurrentFileInfo(uzf, &file_info, nullptr, 0, nullptr, 0, + nullptr, 0), UNZ_OK); + ASSERT_EQ(file_info.size_filename, 14); + + EXPECT_EQ(unzClose(uzf), UNZ_OK); +} + #endif diff --git a/deps/zlib/google/DEPS b/deps/zlib/google/DEPS index 7ec5343ee23953..cb95fcb4277f9e 100644 --- a/deps/zlib/google/DEPS +++ b/deps/zlib/google/DEPS @@ -1,6 +1,7 @@ include_rules = [ '+base', '+build', + '+crypto', '+testing', '+third_party/icu/source/i18n/unicode', '+third_party/zlib/zlib.h', diff --git a/deps/zlib/google/test/data/create_symlink_test_zips.py b/deps/zlib/google/test/data/create_symlink_test_zips.py index 833887acaf7614..cc7c1477ad9ee7 100644 --- a/deps/zlib/google/test/data/create_symlink_test_zips.py +++ b/deps/zlib/google/test/data/create_symlink_test_zips.py @@ -16,6 +16,10 @@ def make_file(zf, path, content): zf.writestr(zipfile.ZipInfo(path), content) +def make_dir(zf, path): + zf.mkdir(path) + + def make_test_zips(): with make_zip('symlinks.zip') as zf: make_file(zf, 'a.txt', 'A') @@ -39,6 +43,11 @@ def make_test_zips(): make_link(zf, 'file', 'link') make_file(zf, 'link', 'Hello world') + with make_zip('symlink_follow_own_link_dir.zip') as zf: + make_dir(zf, 'dir') + make_link(zf, 'dir', 'link') + make_file(zf, 'link/file', 'Hello world') + with make_zip('symlink_duplicate_link.zip') as zf: make_link(zf, 'target_1', 'link') make_link(zf, 'target_2', 'link') diff --git a/deps/zlib/google/test/data/symlink_follow_own_link_dir.zip b/deps/zlib/google/test/data/symlink_follow_own_link_dir.zip new file mode 100644 index 00000000000000..73992f1e6c7585 Binary files /dev/null and b/deps/zlib/google/test/data/symlink_follow_own_link_dir.zip differ diff --git a/deps/zlib/google/test/data/unicode_path_extra.zip b/deps/zlib/google/test/data/unicode_path_extra.zip new file mode 100644 index 00000000000000..bb2b1c76702bb9 Binary files /dev/null and b/deps/zlib/google/test/data/unicode_path_extra.zip differ diff --git a/deps/zlib/google/test/data/unicode_path_extra_overflow.zip b/deps/zlib/google/test/data/unicode_path_extra_overflow.zip new file mode 100644 index 00000000000000..36d525b99b9025 Binary files /dev/null and b/deps/zlib/google/test/data/unicode_path_extra_overflow.zip differ diff --git a/deps/zlib/google/test_data.filelist b/deps/zlib/google/test_data.filelist index 5b29f3665e3e70..bdfec4f3b1801c 100644 --- a/deps/zlib/google/test_data.filelist +++ b/deps/zlib/google/test_data.filelist @@ -25,6 +25,7 @@ test/data/symlink_absolute_path.zip test/data/symlink_duplicate_link.zip test/data/symlink_evil_relative_path.zip test/data/symlink_follow_own_link.zip +test/data/symlink_follow_own_link_dir.zip test/data/symlink_too_large.zip test/data/symlinks.zip test/data/test.zip @@ -37,3 +38,5 @@ test/data/test_encrypted.zip test/data/test_mismatch_size.zip test/data/test_nocompress.zip test/data/test_posix_permissions.zip +test/data/unicode_path_extra.zip +test/data/unicode_path_extra_overflow.zip diff --git a/deps/zlib/google/zip.cc b/deps/zlib/google/zip.cc index 48c821e4219bf7..25240ca8181549 100644 --- a/deps/zlib/google/zip.cc +++ b/deps/zlib/google/zip.cc @@ -14,6 +14,7 @@ #include "base/files/file_util.h" #include "base/functional/bind.h" #include "base/functional/callback.h" +#include "base/containers/flat_set.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" @@ -167,6 +168,8 @@ bool UnzipImpl( return false; } + base::flat_set symlinks; + while (const ZipReader::Entry* const entry = reader.Next()) { if (entry->is_unsafe) { LOG(ERROR) << "Found unsafe entry " << Redact(entry->path) << " in ZIP"; @@ -181,6 +184,24 @@ bool UnzipImpl( continue; } + base::FilePath path = entry->path; + bool is_in_symlinked_dir = false; + while (path.DirName() != path) { + path = path.DirName(); + if (symlinks.contains(path)) { + is_in_symlinked_dir = true; + break; + } + } + if (is_in_symlinked_dir) { + LOG(ERROR) << "Found entry " << Redact(entry->path) + << " in symlinked directory " << Redact(path); + if (!options.continue_on_error) { + return false; + } + continue; + } + if (entry->is_directory) { // It's a directory. if (!directory_creator.Run(entry->path)) { @@ -217,6 +238,7 @@ bool UnzipImpl( } continue; } + symlinks.insert(entry->path); continue; } #endif // defined(OS_POSIX) diff --git a/deps/zlib/google/zip_reader_unittest.cc b/deps/zlib/google/zip_reader_unittest.cc index d77838cb5e728f..aa929074aadfdd 100644 --- a/deps/zlib/google/zip_reader_unittest.cc +++ b/deps/zlib/google/zip_reader_unittest.cc @@ -20,16 +20,18 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/functional/bind.h" -#include "base/hash/md5.h" #include "base/i18n/time_formatting.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/time/time.h" #include "build/build_config.h" +#include "crypto/sha2.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -44,7 +46,8 @@ using ::testing::SizeIs; namespace { -const static std::string kQuuxExpectedMD5 = "d1ae4ac8a17a0e09317113ab284b57a6"; +const static std::string kQuuxExpectedSHA256 = + "8B50F75A113622698B1DBA78B799A0D242F0369DA2E58DA8522A334197241C6E"; class FileWrapper { public: @@ -550,11 +553,11 @@ TEST_F(ZipReaderTest, ExtractToFileAsync_RegularFile) { EXPECT_EQ(0, listener.failure_calls()); EXPECT_LE(1, listener.progress_calls()); - std::string output; - ASSERT_TRUE( - base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), &output)); - const std::string md5 = base::MD5String(output); - EXPECT_EQ(kQuuxExpectedMD5, md5); + std::optional> output = + base::ReadFileToBytes(test_dir_.AppendASCII("quux.txt")); + ASSERT_TRUE(output.has_value()); + const std::string sha256 = base::HexEncode(crypto::SHA256Hash(*output)); + EXPECT_EQ(kQuuxExpectedSHA256, sha256); std::optional file_size = base::GetFileSize(target_file); ASSERT_TRUE(file_size.has_value()); diff --git a/deps/zlib/google/zip_unittest.cc b/deps/zlib/google/zip_unittest.cc index 150844ae3eb712..494ffed2d297d8 100644 --- a/deps/zlib/google/zip_unittest.cc +++ b/deps/zlib/google/zip_unittest.cc @@ -312,8 +312,8 @@ class ZipTest : public PlatformTest { EXPECT_EQ(base::File::FILE_OK, files.GetError()); size_t expected_count = 0; - for (const base::FilePath& path : zip_contents_) { - if (expect_hidden_files || path.BaseName().value()[0] != '.') { + for (const base::FilePath& p : zip_contents_) { + if (expect_hidden_files || p.BaseName().value()[0] != '.') { ++expected_count; } } @@ -1035,6 +1035,14 @@ TEST_F(ZipTest, UnzipSymlinksNoFollowOwnLink) { zip::UnzipSymlinkOption::PRESERVE)); } +TEST_F(ZipTest, UnzipSymlinksNoFollowOwnLinkDir) { + const base::FilePath zip_path = + GetDataDirectory().AppendASCII("symlink_follow_own_link_dir.zip"); + ASSERT_TRUE(base::PathExists(zip_path)); + EXPECT_FALSE(zip::Unzip(zip_path, test_dir_, /*options=*/{}, + zip::UnzipSymlinkOption::PRESERVE)); +} + TEST_F(ZipTest, UnzipSymlinksRejectsDuplicateLink) { const base::FilePath zip_path = GetDataDirectory().AppendASCII("symlink_duplicate_link.zip"); diff --git a/deps/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch b/deps/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch index 73ea055d5bbd30..d69e57c07861e8 100644 --- a/deps/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch +++ b/deps/zlib/patches/0016-minizip-parse-unicode-path-extra-field.patch @@ -35,10 +35,51 @@ Date: Fri May 16 15:48:19 2025 +0200 Change-Id: Ifab65f470736b45b1b51a1cc130a5753a2b20583 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6553931 +commit 9f6e08ef47d3bc9438fdc3b1ab77126a7b36cce9 +Author: Hans Wennborg +Date: Thu Jul 3 17:47:55 2025 +0200 + + [minizip] Fix Unicode Path Extra Field filename length overflow + + If dataSize is too small, fileNameSize would overflow. + + Bug: 428744375 + Change-Id: I714fc1e30cb1634c31cb97ce87be225518368e57 + Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6701714 + +Author: Hans Wennborg +Date: Fri Jul 11 13:04:30 2025 +0200 + + [minizip] Fix potential OOB in unicode path extra field parsing + + The code needs to compare the CRC of the "regular" filename with the + NameCRC32 value in the unicode path extra field. However, the caller may + not have passed in a szFileName buffer large enough to hold the whole + filename, or indeed any buffer at all. + + To fix this, always read the full filename into a temporary buffer, and + compute the CRC on that. + + This also simplifies the logic for writing the filename to szFileName, + at the expense of using a small bit of extra memory, which shouldn't be + an issue for our use cases. + + Bug: 431119343 + Change-Id: Ib06e1009b6e25d2d14e36858385df46d72b7bc3e + Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6725677 + diff --git a/third_party/zlib/contrib/minizip/unzip.c b/third_party/zlib/contrib/minizip/unzip.c index c8a01b23efd42..42677cff82c96 100644 --- a/third_party/zlib/contrib/minizip/unzip.c +++ b/third_party/zlib/contrib/minizip/unzip.c +@@ -64,6 +64,7 @@ + */ + + ++#include + #include + #include + #include @@ -193,6 +193,26 @@ typedef struct Reads a long in LSB order from the given gz_stream. Sets */ @@ -66,7 +107,61 @@ index c8a01b23efd42..42677cff82c96 100644 local int unz64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { -@@ -948,6 +968,62 @@ local int unz64local_GetCurrentFileInfoInternal(unzFile file, +@@ -777,6 +838,7 @@ local int unz64local_GetCurrentFileInfoI + uLong uMagic; + long lSeek=0; + uLong uL; ++ uLong uFileNameCrc; + + if (file==NULL) + return UNZ_PARAMERROR; +@@ -848,21 +910,34 @@ local int unz64local_GetCurrentFileInfoI + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; +- if ((err==UNZ_OK) && (szFileName!=NULL)) ++ if (err==UNZ_OK) + { +- uLong uSizeRead ; +- if (file_info.size_filename 0) + { +- *(szFileName+file_info.size_filename)='\0'; +- uSizeRead = file_info.size_filename; ++ if (ZREAD64(s->z_filefunc, s->filestream, szCurrentFileName, file_info.size_filename) != file_info.size_filename) ++ { ++ err=UNZ_ERRNO; ++ } + } +- else +- uSizeRead = fileNameBufferSize; + +- if ((file_info.size_filename>0) && (fileNameBufferSize>0)) +- if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) +- err=UNZ_ERRNO; +- lSeek -= uSizeRead; ++ uFileNameCrc = crc32(0, (unsigned char*)szCurrentFileName, file_info.size_filename); ++ ++ if (szFileName != NULL) ++ { ++ if (fileNameBufferSize <= file_info.size_filename) ++ { ++ memcpy(szFileName, szCurrentFileName, fileNameBufferSize); ++ } ++ else ++ { ++ memcpy(szFileName, szCurrentFileName, file_info.size_filename); ++ szFileName[file_info.size_filename] = '\0'; ++ } ++ } ++ ++ lSeek -= file_info.size_filename; + } + + // Read extrafield +@@ -948,6 +1023,76 @@ local int unz64local_GetCurrentFileInfoI } } @@ -74,7 +169,15 @@ index c8a01b23efd42..42677cff82c96 100644 + { + int version = 0; + -+ if (unz64local_getByte(&s->z_filefunc, s->filestream, &version) != UNZ_OK) ++ if (dataSize < 1 + 4) ++ { ++ /* dataSize includes version (1 byte), uCrc (4 bytes), and ++ * the filename data. If it's too small, fileNameSize below ++ * would overflow. */ ++ err = UNZ_ERRNO; ++ break; ++ } ++ else if (unz64local_getByte(&s->z_filefunc, s->filestream, &version) != UNZ_OK) + { + err = UNZ_ERRNO; + } @@ -87,16 +190,16 @@ index c8a01b23efd42..42677cff82c96 100644 + } + else + { -+ uLong uCrc, uHeaderCrc, fileNameSize; ++ uLong uCrc, fileNameSize; + + if (unz64local_getLong(&s->z_filefunc, s->filestream, &uCrc) != UNZ_OK) + { + err = UNZ_ERRNO; + } -+ uHeaderCrc = crc32(0, (const unsigned char *)szFileName, file_info.size_filename); -+ fileNameSize = dataSize - (2 * sizeof (short) + 1); ++ fileNameSize = dataSize - (1 + 4); /* 1 for version, 4 for uCrc */ ++ + /* Check CRC against file name in the header. */ -+ if (uHeaderCrc != uCrc) ++ if (uCrc != uFileNameCrc) + { + if (ZSEEK64(s->z_filefunc, s->filestream, fileNameSize, ZLIB_FILEFUNC_SEEK_CUR) != 0) + { @@ -105,24 +208,28 @@ index c8a01b23efd42..42677cff82c96 100644 + } + else + { -+ uLong uSizeRead; -+ + file_info.size_filename = fileNameSize; + -+ if (fileNameSize < fileNameBufferSize) -+ { -+ *(szFileName + fileNameSize) = '\0'; -+ uSizeRead = fileNameSize; -+ } -+ else ++ char szCurrentFileName[UINT16_MAX] = {0}; ++ ++ if (file_info.size_filename > 0) + { -+ uSizeRead = fileNameBufferSize; ++ if (ZREAD64(s->z_filefunc, s->filestream, szCurrentFileName, file_info.size_filename) != file_info.size_filename) ++ { ++ err = UNZ_ERRNO; ++ } + } -+ if ((fileNameSize > 0) && (fileNameBufferSize > 0)) ++ ++ if (szFileName != NULL) + { -+ if (ZREAD64(s->z_filefunc, s->filestream, szFileName, uSizeRead) != uSizeRead) ++ if (fileNameBufferSize <= file_info.size_filename) + { -+ err = UNZ_ERRNO; ++ memcpy(szFileName, szCurrentFileName, fileNameBufferSize); ++ } ++ else ++ { ++ memcpy(szFileName, szCurrentFileName, file_info.size_filename); ++ szFileName[file_info.size_filename] = '\0'; + } + } + } diff --git a/src/zlib_version.h b/src/zlib_version.h index 67856b28ad6146..bb98cfd7fa776a 100644 --- a/src/zlib_version.h +++ b/src/zlib_version.h @@ -2,5 +2,5 @@ // Refer to tools/dep_updaters/update-zlib.sh #ifndef SRC_ZLIB_VERSION_H_ #define SRC_ZLIB_VERSION_H_ -#define ZLIB_VERSION "1.3.1-470d3a2" +#define ZLIB_VERSION "1.3.1-5aa6173" #endif // SRC_ZLIB_VERSION_H_