Index: source/lib/file/archive/archive_zip.cpp =================================================================== --- source/lib/file/archive/archive_zip.cpp +++ source/lib/file/archive/archive_zip.cpp @@ -269,6 +269,11 @@ cd_size = (size_t)read_le32(&m_cd_size); } + off_t getCommentLength() const + { + return (off_t)read_le16(&m_comment_len); + } + private: u32 m_magic; u16 m_diskNum; @@ -521,8 +526,29 @@ io::Operation op(*file.get(), buf, scanSize, ofs); RETURN_STATUS_IF_ERR(io::Run(op)); + // Scanning for ECDR first assumes no comment exists + // (standard case), so ECDR structure exists right at + // end of file + off_t offsetInBlock = scanSize - sizeof(ECDR); + const ECDR* ecdr = NULL; + + for (off_t commentSize = 0; + (commentSize <= offsetInBlock) && !ecdr; + commentSize++) { + const u8 *pECDRTest = buf + offsetInBlock - commentSize; + if(*(u32*)pECDRTest == ecdr_magic) { + // Signature matches, test whether comment + // fills up the whole space following the + // ECDR + ecdr = (const ECDR *)pECDRTest; + if (commentSize != ecdr->getCommentLength()) + { + ecdr = NULL; + } + } + } + // look for ECDR in buffer - const ECDR* ecdr = (const ECDR*)FindRecord(buf, scanSize, buf, ecdr_magic, sizeof(ECDR)); if(!ecdr) return INFO::CANNOT_HANDLE; @@ -535,14 +561,9 @@ const size_t maxScanSize = 66000u; // see below UniqueRange buf(io::Allocate(maxScanSize)); - // expected case: ECDR at EOF; no file comment - Status ret = ScanForEcdr(file, fileSize, (u8*)buf.get(), sizeof(ECDR), cd_numEntries, cd_ofs, cd_size); + Status ret = ScanForEcdr(file, fileSize, (u8*)buf.get(), maxScanSize, cd_numEntries, cd_ofs, cd_size); if(ret == INFO::OK) return INFO::OK; - // worst case: ECDR precedes 64 KiB of file comment - ret = ScanForEcdr(file, fileSize, (u8*)buf.get(), maxScanSize, cd_numEntries, cd_ofs, cd_size); - if(ret == INFO::OK) - return INFO::OK; // both ECDR scans failed - this is not a valid Zip file. io::Operation op(*file.get(), buf.get(), sizeof(LFH)); Index: source/lib/file/archive/tests/test_archive_zip.h =================================================================== --- source/lib/file/archive/tests/test_archive_zip.h +++ source/lib/file/archive/tests/test_archive_zip.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2010 Wildfire Games. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "lib/self_test.h" +#include "lib/file/archive/archive_zip.h" + +class TestArchiveZip : public CxxTest::TestSuite +{ +public: + void test_scan_suspiciousZipFile() { + resultbuffer = ""; + PIArchiveReader testee = + CreateArchiveReader_Zip( + DataDir() / "mods" / "_test.lib" / "test.zip"); + + TS_ASSERT(NULL != testee); + + testee->ReadEntries(TestArchiveZip::ArchiveEntryCallback, 0); + + TS_ASSERT_EQUALS("buildzipwithcomment.sh", resultbuffer); + // delete testee; <- Not used due to shared_ptr + } + +private: + static std::string resultbuffer; + + static void ArchiveEntryCallback( + const VfsPath &path, + const CFileInfo &, + PIArchiveFile, + uintptr_t) { + resultbuffer = path.string8(); + } +}; + +// Implementation of the static buffer used to communicate with ArchiveEntryCallback +std::string TestArchiveZip::resultbuffer; +