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.cc b/deps/zlib/google/zip_reader.cc index f71fcf51778cb9..eb3adcaee35c0b 100644 --- a/deps/zlib/google/zip_reader.cc +++ b/deps/zlib/google/zip_reader.cc @@ -118,6 +118,66 @@ void SetPosixFilePermissions(int fd, int mode) { } #endif +// Callback function for zlib that opens a file stream from a ReaderDelegate. +void* OpenReaderDelegate(void* opaque, const void* /*filename*/, int mode) { + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) != ZLIB_FILEFUNC_MODE_READ) { + return nullptr; + } + // The opaque parameter is the ReaderDelegate pointer. + return opaque; +} + +uLong ReadReaderDelegate(void* opaque, void* stream, void* buf, uLong size) { + ReaderDelegate* delegate = static_cast(opaque); + // SAFETY: raw Read() method from third_party library. + auto span = UNSAFE_BUFFERS( + base::span(static_cast(buf), static_cast(size))); + int64_t bytes_read = delegate->ReadBytes(span); + return (bytes_read < 0) ? 0 : static_cast(bytes_read); +} + +uLong WriteReaderDelegate(void* /*opaque*/, + void* /*stream*/, + const void* /*buf*/, + uLong /*size*/) { + NOTREACHED(); +} + +ZPOS64_T TellReaderDelegate(void* opaque, void* stream) { + ReaderDelegate* delegate = static_cast(opaque); + return delegate->Tell(); +} + +long SeekReaderDelegate(void* opaque, + void* stream, + ZPOS64_T offset, + int origin) { + ReaderDelegate* delegate = static_cast(opaque); + int64_t new_offset = 0; + + if (origin == ZLIB_FILEFUNC_SEEK_SET) { + new_offset = offset; + } else if (origin == ZLIB_FILEFUNC_SEEK_CUR) { + new_offset = delegate->Tell() + offset; + } else if (origin == ZLIB_FILEFUNC_SEEK_END) { + // For SEEK_END, offset is the distance from the end of the file. + new_offset = delegate->GetLength() - offset; + } else { + return -1; + } + + return delegate->Seek(new_offset) ? 0 : -1; +} + +int CloseReaderDelegate(void* opaque, void* stream) { + // We don't own the delegate, so we don't delete it. + return 0; +} + +int ErrorReaderDelegate(void* opaque, void* stream) { + return 0; +} + } // namespace ZipReader::ZipReader() { @@ -165,6 +225,28 @@ bool ZipReader::OpenFromString(const std::string& data) { return OpenInternal(); } +bool ZipReader::OpenFromReaderDelegate(ReaderDelegate* delegate) { + DCHECK(!zip_file_); + DCHECK(delegate); + + zlib_filefunc64_def zip_funcs; + zip_funcs.zopen64_file = OpenReaderDelegate; + zip_funcs.zread_file = ReadReaderDelegate; + zip_funcs.zwrite_file = WriteReaderDelegate; + zip_funcs.ztell64_file = TellReaderDelegate; + zip_funcs.zseek64_file = SeekReaderDelegate; + zip_funcs.zclose_file = CloseReaderDelegate; + zip_funcs.zerror_file = ErrorReaderDelegate; + zip_funcs.opaque = delegate; + + zip_file_ = unzOpen2_64(nullptr, &zip_funcs); + if (!zip_file_) { + return false; + } + + return OpenInternal(); +} + void ZipReader::Close() { if (zip_file_) { if (const UnzipError err{unzClose(zip_file_)}; err != UNZ_OK) { diff --git a/deps/zlib/google/zip_reader.h b/deps/zlib/google/zip_reader.h index 3caacc0bedd8af..26c4f716c14cf2 100644 --- a/deps/zlib/google/zip_reader.h +++ b/deps/zlib/google/zip_reader.h @@ -54,6 +54,27 @@ class WriterDelegate { virtual void OnError() {} }; +// A delegate interface used to read data from the ZIP archive. +class ReaderDelegate { + public: + virtual ~ReaderDelegate() = default; + + // Invoked to read the next chunk of data. + // Returns the number of bytes read and -1 on error. + virtual int64_t ReadBytes(base::span data) = 0; + + // Invoked to seek to the specified offset. Returns true on success. + virtual bool Seek(int64_t offset) = 0; + + // Invoked to get the current offset. + // Returns the current offset and -1 on error. + virtual int64_t Tell() = 0; + + // Invoked to get the size of the file. + // Returns the file size and -1 on error. + virtual int64_t GetLength() = 0; +}; + // This class is used for reading ZIP archives. A typical use case of this class // is to scan entries in a ZIP archive and extract them. The code will look // like: @@ -168,6 +189,10 @@ class ZipReader { // string until it finishes extracting files. bool OpenFromString(const std::string& data); + // Opens the ZIP archive from a reader delegate. The delegate must outlive + // the ZipReader. Returns true on success. + bool OpenFromReaderDelegate(ReaderDelegate* delegate); + // Closes the currently opened ZIP archive. This function is called in the // destructor of the class, so you usually don't need to call this. void Close(); diff --git a/deps/zlib/google/zip_reader_unittest.cc b/deps/zlib/google/zip_reader_unittest.cc index d77838cb5e728f..10ad64b4ce9b24 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: @@ -148,6 +151,38 @@ const zip::ZipReader::Entry* LocateAndOpenEntry( using Paths = std::vector; +// A reader delegate that reads from a string. +class StringReaderDelegate : public zip::ReaderDelegate { + public: + explicit StringReaderDelegate(std::string data) : data_(std::move(data)) {} + + int64_t ReadBytes(base::span data) override { + auto full_span = base::as_byte_span(data_); + if (offset_ >= full_span.size()) + return 0; + auto source = full_span.subspan(offset_); + size_t to_read = std::min(data.size(), source.size()); + data.first(to_read).copy_from(source.first(to_read)); + offset_ += to_read; + return to_read; + } + + bool Seek(int64_t offset) override { + if (offset < 0 || static_cast(offset) > data_.size()) + return false; + offset_ = offset; + return true; + } + + int64_t Tell() override { return offset_; } + + int64_t GetLength() override { return data_.size(); } + + private: + std::string data_; + size_t offset_ = 0; +}; + } // namespace namespace zip { @@ -224,6 +259,39 @@ TEST_F(ZipReaderTest, Open_ValidZipPlatformFile) { EXPECT_TRUE(reader.ok()); } +TEST_F(ZipReaderTest, OpenFromReaderDelegate) { + const char kTestData[] = + "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\xa4\x66\x24\x41\x13\xe8" + "\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00\x1c\x00\x74\x65" + "\x73\x74\x2e\x74\x78\x74\x55\x54\x09\x00\x03\x34\x89\x45\x50\x34" + "\x89\x45\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13" + "\x00\x00\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74" + "\x2e\x0a\x50\x4b\x01\x02\x1e\x03\x0a\x00\x00\x00\x00\x00\xa4\x66" + "\x24\x41\x13\xe8\xcb\x27\x10\x00\x00\x00\x10\x00\x00\x00\x08\x00" + "\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\xa4\x81\x00\x00\x00\x00" + "\x74\x65\x73\x74\x2e\x74\x78\x74\x55\x54\x05\x00\x03\x34\x89\x45" + "\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13\x00\x00" + "\x50\x4b\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4e\x00\x00\x00" + "\x52\x00\x00\x00\x00\x00"; + std::string data(kTestData, std::size(kTestData)); + auto delegate = std::make_unique(data); + + ZipReader reader; + EXPECT_FALSE(reader.ok()); + EXPECT_TRUE(reader.OpenFromReaderDelegate(delegate.get())); + EXPECT_TRUE(reader.ok()); + + base::FilePath target_path(FILE_PATH_LITERAL("test.txt")); + ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path)); + ASSERT_TRUE(ExtractCurrentEntryToFilePath(&reader, + test_dir_.AppendASCII("test.txt"))); + + std::string actual; + ASSERT_TRUE( + base::ReadFileToString(test_dir_.AppendASCII("test.txt"), &actual)); + EXPECT_EQ(std::string("This is a test.\n"), actual); +} + TEST_F(ZipReaderTest, Open_NonExistentFile) { ZipReader reader; EXPECT_FALSE(reader.ok()); @@ -550,11 +618,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..24b241fe6ef844 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-63d7e16" #endif // SRC_ZLIB_VERSION_H_