diff options
author | Kenny Root <kroot@google.com> | 2013-09-20 00:05:16 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-09-20 00:05:16 +0000 |
commit | 261d3cda32b28782d894be0244e617f78182ee3b (patch) | |
tree | 4e85dfadf2e0075c46f2d641312954226a1810e8 /runtime | |
parent | 0f72e4136aecaf6976fdb55916bbd7b6d5c9c77b (diff) | |
parent | 72fcca2477e02da2d3970aefc75465ba1f20ce9c (diff) | |
download | art-261d3cda32b28782d894be0244e617f78182ee3b.zip art-261d3cda32b28782d894be0244e617f78182ee3b.tar.gz art-261d3cda32b28782d894be0244e617f78182ee3b.tar.bz2 |
Merge "Reconcile differences between zip implementations" into klp-dev
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/zip_archive.cc | 113 | ||||
-rw-r--r-- | runtime/zip_archive.h | 24 |
2 files changed, 100 insertions, 37 deletions
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index fc26f0f..c3167e5 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -60,7 +60,7 @@ uint32_t ZipEntry::GetCrc32() { return Le32ToHost(ptr_ + ZipArchive::kCDECRC); } -off_t ZipEntry::GetDataOffset() { +off64_t ZipEntry::GetDataOffset() { // All we have is the offset to the Local File Header, which is // variable size, so we have to read the contents of the struct to // figure out where the actual data starts. @@ -69,14 +69,14 @@ off_t ZipEntry::GetDataOffset() { // somebody trying to map the compressed or uncompressed data runs // off the end of the mapped region. - off_t dir_offset = zip_archive_->dir_offset_; + off64_t dir_offset = zip_archive_->dir_offset_; int64_t lfh_offset = Le32ToHost(ptr_ + ZipArchive::kCDELocalOffset); if (lfh_offset + ZipArchive::kLFHLen >= dir_offset) { LOG(WARNING) << "Zip: bad LFH offset in zip"; return -1; } - if (lseek(zip_archive_->fd_, lfh_offset, SEEK_SET) != lfh_offset) { + if (lseek64(zip_archive_->fd_, lfh_offset, SEEK_SET) != lfh_offset) { PLOG(WARNING) << "Zip: failed seeking to LFH at offset " << lfh_offset; return -1; } @@ -93,7 +93,13 @@ off_t ZipEntry::GetDataOffset() { return -1; } - off_t data_offset = (lfh_offset + ZipArchive::kLFHLen + uint32_t gpbf = Le16ToHost(lfh_buf + ZipArchive::kLFHGPBFlags); + if ((gpbf & ZipArchive::kGPFUnsupportedMask) != 0) { + LOG(WARNING) << "Invalid General Purpose Bit Flag: " << gpbf; + return -1; + } + + off64_t data_offset = (lfh_offset + ZipArchive::kLFHLen + Le16ToHost(lfh_buf + ZipArchive::kLFHNameLen) + Le16ToHost(lfh_buf + ZipArchive::kLFHExtraLen)); if (data_offset >= dir_offset) { @@ -103,7 +109,7 @@ off_t ZipEntry::GetDataOffset() { // check lengths - if (static_cast<off_t>(data_offset + GetCompressedLength()) > dir_offset) { + if (static_cast<off64_t>(data_offset + GetCompressedLength()) > dir_offset) { LOG(WARNING) << "Zip: bad compressed length in zip " << "(" << data_offset << " + " << GetCompressedLength() << " > " << dir_offset << ")"; @@ -111,7 +117,7 @@ off_t ZipEntry::GetDataOffset() { } if (GetCompressionMethod() == kCompressStored - && static_cast<off_t>(data_offset + GetUncompressedLength()) > dir_offset) { + && static_cast<off64_t>(data_offset + GetUncompressedLength()) > dir_offset) { LOG(WARNING) << "Zip: bad uncompressed length in zip " << "(" << data_offset << " + " << GetUncompressedLength() << " > " << dir_offset << ")"; @@ -263,12 +269,12 @@ bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size) { if (size == 0) { return true; } - off_t data_offset = GetDataOffset(); + off64_t data_offset = GetDataOffset(); if (data_offset == -1) { LOG(WARNING) << "Zip: data_offset=" << data_offset; return false; } - if (lseek(zip_archive_->fd_, data_offset, SEEK_SET) != data_offset) { + if (lseek64(zip_archive_->fd_, data_offset, SEEK_SET) != data_offset) { PLOG(WARNING) << "Zip: lseek to data at " << data_offset << " failed"; return false; } @@ -378,10 +384,40 @@ bool ZipArchive::MapCentralDirectory() { /* * Get and test file length. */ - off_t file_length = lseek(fd_, 0, SEEK_END); + off64_t file_length = lseek64(fd_, 0, SEEK_END); if (file_length < kEOCDLen) { - LOG(WARNING) << "Zip: length " << file_length << " is too small to be zip"; - return false; + LOG(WARNING) << "Zip: length " << file_length << " is too small to be zip"; + return false; + } + + size_t read_amount = kMaxEOCDSearch; + if (file_length < off64_t(read_amount)) { + read_amount = file_length; + } + + UniquePtr<uint8_t[]> scan_buf(new uint8_t[read_amount]); + if (scan_buf.get() == NULL) { + return false; + } + + /* + * Make sure this is a Zip archive. + */ + if (lseek64(fd_, 0, SEEK_SET) != 0) { + PLOG(WARNING) << "seek to start failed: "; + return false; + } + + ssize_t actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), sizeof(int32_t))); + if (actual != static_cast<ssize_t>(sizeof(int32_t))) { + PLOG(INFO) << "couldn't read first signature from zip archive: "; + return false; + } + + unsigned int header = Le32ToHost(scan_buf.get()); + if (header != kLFHSignature) { + LOG(VERBOSE) << "Not a Zip archive (found " << std::hex << header << ")"; + return false; } // Perform the traditional EOCD snipe hunt. @@ -394,25 +430,15 @@ bool ZipArchive::MapCentralDirectory() { // to determine the extent of the CD. // // We start by pulling in the last part of the file. - size_t read_amount = kMaxEOCDSearch; - if (file_length < off_t(read_amount)) { - read_amount = file_length; - } + off64_t search_start = file_length - read_amount; - UniquePtr<uint8_t[]> scan_buf(new uint8_t[read_amount]); - if (scan_buf.get() == NULL) { - return false; - } - - off_t search_start = file_length - read_amount; - - if (lseek(fd_, search_start, SEEK_SET) != search_start) { + if (lseek64(fd_, search_start, SEEK_SET) != search_start) { PLOG(WARNING) << "Zip: seek " << search_start << " failed"; return false; } - ssize_t actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), read_amount)); - if (actual == -1) { - PLOG(WARNING) << "Zip: read " << read_amount << " failed"; + actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), read_amount)); + if (actual != static_cast<ssize_t>(read_amount)) { + PLOG(WARNING) << "Zip: read " << actual << ", expected " << read_amount << ". failed"; return false; } @@ -432,16 +458,20 @@ bool ZipArchive::MapCentralDirectory() { return false; } - off_t eocd_offset = search_start + i; + off64_t eocd_offset = search_start + i; const byte* eocd_ptr = scan_buf.get() + i; DCHECK(eocd_offset < file_length); // Grab the CD offset and size, and the number of entries in the // archive. Verify that they look reasonable. + uint16_t disk_number = Le16ToHost(eocd_ptr + kEOCDDiskNumber); + uint16_t disk_with_central_dir = Le16ToHost(eocd_ptr + kEOCDDiskNumberForCD); uint16_t num_entries = Le16ToHost(eocd_ptr + kEOCDNumEntries); + uint16_t total_num_entries = Le16ToHost(eocd_ptr + kEOCDTotalNumEntries); uint32_t dir_size = Le32ToHost(eocd_ptr + kEOCDSize); uint32_t dir_offset = Le32ToHost(eocd_ptr + kEOCDFileOffset); + uint16_t comment_size = Le16ToHost(eocd_ptr + kEOCDCommentSize); if ((uint64_t) dir_offset + (uint64_t) dir_size > (uint64_t) eocd_offset) { LOG(WARNING) << "Zip: bad offsets (" @@ -453,6 +483,16 @@ bool ZipArchive::MapCentralDirectory() { if (num_entries == 0) { LOG(WARNING) << "Zip: empty archive?"; return false; + } else if (num_entries != total_num_entries || disk_number != 0 || disk_with_central_dir != 0) { + LOG(WARNING) << "spanned archives not supported"; + return false; + } + + // Check to see if comment is a sane size + if ((comment_size > (file_length - kEOCDLen)) + || (eocd_offset > (file_length - kEOCDLen) - comment_size)) { + LOG(WARNING) << "comment size runs off end of file"; + return false; } // It all looks good. Create a mapping for the CD. @@ -489,14 +529,27 @@ bool ZipArchive::Parse() { return false; } - uint16_t filename_len = Le16ToHost(ptr + kCDENameLen); + uint16_t gpbf = Le16ToHost(ptr + kCDEGPBFlags); + if ((gpbf & kGPFUnsupportedMask) != 0) { + LOG(WARNING) << "Invalid General Purpose Bit Flag: " << gpbf; + return false; + } + + uint16_t name_len = Le16ToHost(ptr + kCDENameLen); uint16_t extra_len = Le16ToHost(ptr + kCDEExtraLen); uint16_t comment_len = Le16ToHost(ptr + kCDECommentLen); // add the CDE filename to the hash table const char* name = reinterpret_cast<const char*>(ptr + kCDELen); - dir_entries_.Put(StringPiece(name, filename_len), ptr); - ptr += kCDELen + filename_len + extra_len + comment_len; + + // Check name for NULL characters + if (memchr(name, 0, name_len) != NULL) { + LOG(WARNING) << "Filename contains NUL byte"; + return false; + } + + dir_entries_.Put(StringPiece(name, name_len), ptr); + ptr += kCDELen + name_len + extra_len + comment_len; if (ptr > cd_ptr + cd_length) { LOG(WARNING) << "Zip: bad CD advance " << "(" << ptr << " vs " << (cd_ptr + cd_length) << ") " diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index d648517..d9ccba2 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -58,7 +58,7 @@ class ZipEntry { uint32_t GetCompressedLength(); // returns -1 on error - off_t GetDataOffset(); + off64_t GetDataOffset(); const ZipArchive* zip_archive_; @@ -72,22 +72,28 @@ class ZipEntry { class ZipArchive { public: // Zip file constants. - static const uint32_t kEOCDSignature = 0x06054b50; - static const int32_t kEOCDLen = 22; - static const int32_t kEOCDNumEntries = 8; // offset to #of entries in file - static const int32_t kEOCDSize = 12; // size of the central directory - static const int32_t kEOCDFileOffset = 16; // offset to central directory + static const uint32_t kEOCDSignature = 0x06054b50; + static const int32_t kEOCDLen = 22; + static const int32_t kEOCDDiskNumber = 4; // number of the current disk + static const int32_t kEOCDDiskNumberForCD = 6; // disk number with the Central Directory + static const int32_t kEOCDNumEntries = 8; // offset to #of entries in file + static const int32_t kEOCDTotalNumEntries = 10; // offset to total #of entries in spanned archives + static const int32_t kEOCDSize = 12; // size of the central directory + static const int32_t kEOCDFileOffset = 16; // offset to central directory + static const int32_t kEOCDCommentSize = 20; // offset to the length of the file comment static const int32_t kMaxCommentLen = 65535; // longest possible in uint16_t static const int32_t kMaxEOCDSearch = (kMaxCommentLen + kEOCDLen); static const uint32_t kLFHSignature = 0x04034b50; static const int32_t kLFHLen = 30; // excluding variable-len fields + static const int32_t kLFHGPBFlags = 6; // offset to GPB flags static const int32_t kLFHNameLen = 26; // offset to filename length static const int32_t kLFHExtraLen = 28; // offset to extra length static const uint32_t kCDESignature = 0x02014b50; static const int32_t kCDELen = 46; // excluding variable-len fields + static const int32_t kCDEGPBFlags = 8; // offset to GPB flags static const int32_t kCDEMethod = 10; // offset to compression method static const int32_t kCDEModWhen = 12; // offset to modification timestamp static const int32_t kCDECRC = 16; // offset to entry CRC @@ -98,6 +104,10 @@ class ZipArchive { static const int32_t kCDECommentLen = 32; // offset to comment length static const int32_t kCDELocalOffset = 42; // offset to local hdr + // General Purpose Bit Flag + static const int32_t kGPFEncryptedFlag = (1 << 0); + static const int32_t kGPFUnsupportedMask = (kGPFEncryptedFlag); + // return new ZipArchive instance on success, NULL on error. static ZipArchive* Open(const std::string& filename); static ZipArchive* OpenFromFd(int fd); @@ -117,7 +127,7 @@ class ZipArchive { int fd_; uint16_t num_entries_; - off_t dir_offset_; + off64_t dir_offset_; UniquePtr<MemMap> dir_map_; typedef SafeMap<StringPiece, const byte*> DirEntries; DirEntries dir_entries_; |