1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /*
7  * This module implements a simple archive extractor for the PKZIP format.
8  *
9  * The underlying nsZipArchive is NOT thread-safe. Do not pass references
10  * or pointers to it across thread boundaries.
11  */
12 
13 #define READTYPE int32_t
14 #include "zlib.h"
15 #ifdef MOZ_JAR_BROTLI
16 #include "brotli/decode.h"  // brotli
17 #endif
18 #include "nsISupportsUtils.h"
19 #include "prio.h"
20 #include "plstr.h"
21 #include "mozilla/Attributes.h"
22 #include "mozilla/Logging.h"
23 #include "mozilla/UniquePtrExtensions.h"
24 #include "stdlib.h"
25 #include "nsWildCard.h"
26 #include "nsZipArchive.h"
27 #include "nsString.h"
28 #include "prenv.h"
29 #if defined(XP_WIN)
30 #include <windows.h>
31 #endif
32 
33 // For placement new used for arena allocations of zip file list
34 #include <new>
35 #define ZIP_ARENABLOCKSIZE (1 * 1024)
36 
37 #ifdef XP_UNIX
38 #include <sys/mman.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <limits.h>
42 #include <unistd.h>
43 #elif defined(XP_WIN)
44 #include <io.h>
45 #endif
46 
47 #ifdef __SYMBIAN32__
48 #include <sys/syslimits.h>
49 #endif /*__SYMBIAN32__*/
50 
51 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
52 #ifndef S_IFMT
53 #define S_IFMT 0170000
54 #endif
55 #ifndef S_IFLNK
56 #define S_IFLNK 0120000
57 #endif
58 #ifndef PATH_MAX
59 #define PATH_MAX 1024
60 #endif
61 #endif /* XP_UNIX */
62 
63 #ifdef XP_WIN
64 #include "private/pprio.h"  // To get PR_ImportFile
65 #endif
66 
67 using namespace mozilla;
68 
69 static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */
70 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
71 static const uint16_t kSyntheticTime = 0;
72 static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9));
73 
74 static uint16_t xtoint(const uint8_t *ii);
75 static uint32_t xtolong(const uint8_t *ll);
76 static uint32_t HashName(const char *aName, uint16_t nameLen);
77 
78 class ZipArchiveLogger {
79  public:
Write(const nsACString & zip,const char * entry) const80   void Write(const nsACString &zip, const char *entry) const {
81     if (!fd) {
82       char *env = PR_GetEnv("MOZ_JAR_LOG_FILE");
83       if (!env) return;
84 
85       nsCOMPtr<nsIFile> logFile;
86       nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false,
87                                     getter_AddRefs(logFile));
88       if (NS_FAILED(rv)) return;
89 
90       // Create the log file and its parent directory (in case it doesn't exist)
91       rv = logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
92       if (NS_FAILED(rv)) return;
93 
94       PRFileDesc *file;
95 #ifdef XP_WIN
96       // PR_APPEND is racy on Windows, so open a handle ourselves with flags
97       // that will work, and use PR_ImportFile to make it a PRFileDesc. This can
98       // go away when bug 840435 is fixed.
99       nsAutoString path;
100       logFile->GetPath(path);
101       if (path.IsEmpty()) return;
102       HANDLE handle =
103           CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE, nullptr,
104                       OPEN_ALWAYS, 0, nullptr);
105       if (handle == INVALID_HANDLE_VALUE) return;
106       file = PR_ImportFile((PROsfd)handle);
107       if (!file) return;
108 #else
109       rv = logFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_APPEND,
110                                      0644, &file);
111       if (NS_FAILED(rv)) return;
112 #endif
113       fd = file;
114     }
115     nsCString buf(zip);
116     buf.Append(' ');
117     buf.Append(entry);
118     buf.Append('\n');
119     PR_Write(fd, buf.get(), buf.Length());
120   }
121 
AddRef()122   void AddRef() {
123     MOZ_ASSERT(refCnt >= 0);
124     ++refCnt;
125   }
126 
Release()127   void Release() {
128     MOZ_ASSERT(refCnt > 0);
129     if ((0 == --refCnt) && fd) {
130       PR_Close(fd);
131       fd = nullptr;
132     }
133   }
134 
135  private:
136   int refCnt;
137   mutable PRFileDesc *fd;
138 };
139 
140 static ZipArchiveLogger zipLog;
141 
142 //***********************************************************
143 // For every inflation the following allocations are done:
144 // malloc(1 * 9520)
145 // malloc(32768 * 1)
146 //***********************************************************
147 
gZlibInit(z_stream * zs)148 nsresult gZlibInit(z_stream *zs) {
149   memset(zs, 0, sizeof(z_stream));
150   int zerr = inflateInit2(zs, -MAX_WBITS);
151   if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
152 
153   return NS_OK;
154 }
155 
nsZipHandle()156 nsZipHandle::nsZipHandle()
157     : mFileData(nullptr),
158       mLen(0),
159       mMap(nullptr),
160       mRefCnt(0),
161       mFileStart(nullptr),
162       mTotalLen(0) {}
163 
164 NS_IMPL_ADDREF(nsZipHandle)
NS_IMPL_RELEASE(nsZipHandle)165 NS_IMPL_RELEASE(nsZipHandle)
166 
167 nsresult nsZipHandle::Init(nsIFile *file, nsZipHandle **ret, PRFileDesc **aFd) {
168   mozilla::AutoFDClose fd;
169   int32_t flags = PR_RDONLY;
170 #if defined(XP_WIN)
171   flags |= nsIFile::OS_READAHEAD;
172 #endif
173   nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget());
174   if (NS_FAILED(rv)) return rv;
175 
176   int64_t size = PR_Available64(fd);
177   if (size >= INT32_MAX) return NS_ERROR_FILE_TOO_BIG;
178 
179   PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
180   if (!map) return NS_ERROR_FAILURE;
181 
182   uint8_t *buf = (uint8_t *)PR_MemMap(map, 0, (uint32_t)size);
183   // Bug 525755: PR_MemMap fails when fd points at something other than a normal
184   // file.
185   if (!buf) {
186     PR_CloseFileMap(map);
187     return NS_ERROR_FAILURE;
188   }
189 
190   RefPtr<nsZipHandle> handle = new nsZipHandle();
191   if (!handle) {
192     PR_MemUnmap(buf, (uint32_t)size);
193     PR_CloseFileMap(map);
194     return NS_ERROR_OUT_OF_MEMORY;
195   }
196 
197 #if defined(XP_WIN)
198   if (aFd) {
199     *aFd = fd.forget();
200   }
201 #else
202   handle->mNSPRFileDesc = fd.forget();
203 #endif
204   handle->mMap = map;
205   handle->mFile.Init(file);
206   handle->mTotalLen = (uint32_t)size;
207   handle->mFileStart = buf;
208   rv = handle->findDataStart();
209   if (NS_FAILED(rv)) {
210     PR_MemUnmap(buf, (uint32_t)size);
211     PR_CloseFileMap(map);
212     return rv;
213   }
214   handle.forget(ret);
215   return NS_OK;
216 }
217 
Init(nsZipArchive * zip,const char * entry,nsZipHandle ** ret)218 nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
219                            nsZipHandle **ret) {
220   RefPtr<nsZipHandle> handle = new nsZipHandle();
221   if (!handle) return NS_ERROR_OUT_OF_MEMORY;
222 
223   handle->mBuf = new nsZipItemPtr<uint8_t>(zip, entry);
224   if (!handle->mBuf) return NS_ERROR_OUT_OF_MEMORY;
225 
226   if (!handle->mBuf->Buffer()) return NS_ERROR_UNEXPECTED;
227 
228   handle->mMap = nullptr;
229   handle->mFile.Init(zip, entry);
230   handle->mTotalLen = handle->mBuf->Length();
231   handle->mFileStart = handle->mBuf->Buffer();
232   nsresult rv = handle->findDataStart();
233   if (NS_FAILED(rv)) {
234     return rv;
235   }
236   handle.forget(ret);
237   return NS_OK;
238 }
239 
Init(const uint8_t * aData,uint32_t aLen,nsZipHandle ** aRet)240 nsresult nsZipHandle::Init(const uint8_t *aData, uint32_t aLen,
241                            nsZipHandle **aRet) {
242   RefPtr<nsZipHandle> handle = new nsZipHandle();
243 
244   handle->mFileStart = aData;
245   handle->mTotalLen = aLen;
246   nsresult rv = handle->findDataStart();
247   if (NS_FAILED(rv)) {
248     return rv;
249   }
250   handle.forget(aRet);
251   return NS_OK;
252 }
253 
254 // This function finds the start of the ZIP data. If the file is a regular ZIP,
255 // this is just the start of the file. If the file is a CRX file, the start of
256 // the data is after the CRX header.
257 // CRX header reference: (CRX version 2)
258 //    Header requires little-endian byte ordering with 4-byte alignment.
259 //    32 bits       : magicNumber   - Defined as a |char m[] = "Cr24"|.
260 //                                    Equivilant to |uint32_t m = 0x34327243|.
261 //    32 bits       : version       - Unsigned integer representing the CRX file
262 //                                    format version. Currently equal to 2.
263 //    32 bits       : pubKeyLength  - Unsigned integer representing the length
264 //                                    of the public key in bytes.
265 //    32 bits       : sigLength     - Unsigned integer representing the length
266 //                                    of the signature in bytes.
267 //    pubKeyLength  : publicKey     - Contents of the author's public key.
268 //    sigLength     : signature     - Signature of the ZIP content.
269 //                                    Signature is created using the RSA
270 //                                    algorithm with the SHA-1 hash function.
findDataStart()271 nsresult nsZipHandle::findDataStart() {
272   // In the CRX header, integers are 32 bits. Our pointer to the file is of
273   // type |uint8_t|, which is guaranteed to be 8 bits.
274   const uint32_t CRXIntSize = 4;
275 
276   MOZ_WIN_MEM_TRY_BEGIN
277   if (mTotalLen > CRXIntSize * 4 && xtolong(mFileStart) == kCRXMagic) {
278     const uint8_t *headerData = mFileStart;
279     headerData += CRXIntSize * 2;  // Skip magic number and version number
280     uint32_t pubKeyLength = xtolong(headerData);
281     headerData += CRXIntSize;
282     uint32_t sigLength = xtolong(headerData);
283     uint32_t headerSize = CRXIntSize * 4 + pubKeyLength + sigLength;
284     if (mTotalLen > headerSize) {
285       mLen = mTotalLen - headerSize;
286       mFileData = mFileStart + headerSize;
287       return NS_OK;
288     }
289   }
290   mLen = mTotalLen;
291   mFileData = mFileStart;
292   MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
293   return NS_OK;
294 }
295 
SizeOfMapping()296 int64_t nsZipHandle::SizeOfMapping() { return mTotalLen; }
297 
GetNSPRFileDesc(PRFileDesc ** aNSPRFileDesc)298 nsresult nsZipHandle::GetNSPRFileDesc(PRFileDesc **aNSPRFileDesc) {
299   if (!aNSPRFileDesc) {
300     return NS_ERROR_ILLEGAL_VALUE;
301   }
302 
303   *aNSPRFileDesc = mNSPRFileDesc;
304   if (!mNSPRFileDesc) {
305     return NS_ERROR_NOT_AVAILABLE;
306   }
307 
308   return NS_OK;
309 }
310 
~nsZipHandle()311 nsZipHandle::~nsZipHandle() {
312   if (mMap) {
313     PR_MemUnmap((void *)mFileStart, mTotalLen);
314     PR_CloseFileMap(mMap);
315   }
316   mFileStart = nullptr;
317   mFileData = nullptr;
318   mMap = nullptr;
319   mBuf = nullptr;
320 }
321 
322 //***********************************************************
323 //      nsZipArchive  --  public methods
324 //***********************************************************
325 
326 //---------------------------------------------
327 //  nsZipArchive::OpenArchive
328 //---------------------------------------------
OpenArchive(nsZipHandle * aZipHandle,PRFileDesc * aFd)329 nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd) {
330   mFd = aZipHandle;
331 
332   //-- get table of contents for archive
333   nsresult rv = BuildFileList(aFd);
334   if (NS_SUCCEEDED(rv)) {
335     if (aZipHandle->mFile) aZipHandle->mFile.GetURIString(mURI);
336   }
337   return rv;
338 }
339 
OpenArchive(nsIFile * aFile)340 nsresult nsZipArchive::OpenArchive(nsIFile *aFile) {
341   RefPtr<nsZipHandle> handle;
342 #if defined(XP_WIN)
343   mozilla::AutoFDClose fd;
344   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle), &fd.rwget());
345 #else
346   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
347 #endif
348   if (NS_FAILED(rv)) return rv;
349 
350 #if defined(XP_WIN)
351   return OpenArchive(handle, fd.get());
352 #else
353   return OpenArchive(handle);
354 #endif
355 }
356 
357 //---------------------------------------------
358 //  nsZipArchive::Test
359 //---------------------------------------------
Test(const char * aEntryName)360 nsresult nsZipArchive::Test(const char *aEntryName) {
361   nsZipItem *currItem;
362 
363   if (aEntryName)  // only test specified item
364   {
365     currItem = GetItem(aEntryName);
366     if (!currItem) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
367     //-- don't test (synthetic) directory items
368     if (currItem->IsDirectory()) return NS_OK;
369     return ExtractFile(currItem, 0, 0);
370   }
371 
372   // test all items in archive
373   for (auto *item : mFiles) {
374     for (currItem = item; currItem; currItem = currItem->next) {
375       //-- don't test (synthetic) directory items
376       if (currItem->IsDirectory()) continue;
377       nsresult rv = ExtractFile(currItem, 0, 0);
378       if (rv != NS_OK) return rv;
379     }
380   }
381 
382   return NS_OK;
383 }
384 
385 //---------------------------------------------
386 //  nsZipArchive::CloseArchive
387 //---------------------------------------------
CloseArchive()388 nsresult nsZipArchive::CloseArchive() {
389   if (mFd) {
390     mArena.Clear();
391     mFd = nullptr;
392   }
393 
394   // CAUTION:
395   // We don't need to delete each of the nsZipItem as the memory for
396   // the zip item and the filename it holds are both allocated from the Arena.
397   // Hence, destroying the Arena is like destroying all the memory
398   // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
399   // anything more than cleaning up memory, we should start calling it.
400   // Let us also cleanup the mFiles table for re-use on the next 'open' call
401   memset(mFiles, 0, sizeof(mFiles));
402   mBuiltSynthetics = false;
403   return NS_OK;
404 }
405 
406 //---------------------------------------------
407 // nsZipArchive::GetItem
408 //---------------------------------------------
GetItem(const char * aEntryName)409 nsZipItem *nsZipArchive::GetItem(const char *aEntryName) {
410   if (aEntryName) {
411     uint32_t len = strlen(aEntryName);
412     //-- If the request is for a directory, make sure that synthetic entries
413     //-- are created for the directories without their own entry.
414     if (!mBuiltSynthetics) {
415       if ((len > 0) && (aEntryName[len - 1] == '/')) {
416         if (BuildSynthetics() != NS_OK) return 0;
417       }
418     }
419     MOZ_WIN_MEM_TRY_BEGIN
420     nsZipItem *item = mFiles[HashName(aEntryName, len)];
421     while (item) {
422       if ((len == item->nameLength) &&
423           (!memcmp(aEntryName, item->Name(), len))) {
424         // Successful GetItem() is a good indicator that the file is about to be
425         // read
426         zipLog.Write(mURI, aEntryName);
427         return item;  //-- found it
428       }
429       item = item->next;
430     }
431     MOZ_WIN_MEM_TRY_CATCH(return nullptr)
432   }
433   return nullptr;
434 }
435 
436 //---------------------------------------------
437 // nsZipArchive::ExtractFile
438 // This extracts the item to the filehandle provided.
439 // If 'aFd' is null, it only tests the extraction.
440 // On extraction error(s) it removes the file.
441 //---------------------------------------------
ExtractFile(nsZipItem * item,nsIFile * outFile,PRFileDesc * aFd)442 nsresult nsZipArchive::ExtractFile(nsZipItem *item, nsIFile *outFile,
443                                    PRFileDesc *aFd) {
444   if (!item) return NS_ERROR_ILLEGAL_VALUE;
445   if (!mFd) return NS_ERROR_FAILURE;
446 
447   // Directory extraction is handled in nsJAR::Extract,
448   // so the item to be extracted should never be a directory
449   MOZ_ASSERT(!item->IsDirectory());
450 
451   Bytef outbuf[ZIP_BUFLEN];
452 
453   nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
454 
455   nsresult rv = NS_OK;
456 
457   while (true) {
458     uint32_t count = 0;
459     uint8_t *buf = cursor.Read(&count);
460     if (!buf) {
461       nsZipArchive::sFileCorruptedReason =
462           "nsZipArchive: Read() failed to return a buffer";
463       rv = NS_ERROR_FILE_CORRUPTED;
464       break;
465     }
466     if (count == 0) {
467       break;
468     }
469 
470     if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
471       rv = NS_ERROR_FILE_DISK_FULL;
472       break;
473     }
474   }
475 
476   //-- delete the file on errors
477   if (aFd) {
478     PR_Close(aFd);
479     if (NS_FAILED(rv) && outFile) {
480       outFile->Remove(false);
481     }
482   }
483 
484   return rv;
485 }
486 
487 //---------------------------------------------
488 // nsZipArchive::FindInit
489 //---------------------------------------------
FindInit(const char * aPattern,nsZipFind ** aFind)490 nsresult nsZipArchive::FindInit(const char *aPattern, nsZipFind **aFind) {
491   if (!aFind) return NS_ERROR_ILLEGAL_VALUE;
492 
493   // null out param in case an error happens
494   *aFind = nullptr;
495 
496   bool regExp = false;
497   char *pattern = 0;
498 
499   // Create synthetic directory entries on demand
500   nsresult rv = BuildSynthetics();
501   if (rv != NS_OK) return rv;
502 
503   // validate the pattern
504   if (aPattern) {
505     switch (NS_WildCardValid((char *)aPattern)) {
506       case INVALID_SXP:
507         return NS_ERROR_ILLEGAL_VALUE;
508 
509       case NON_SXP:
510         regExp = false;
511         break;
512 
513       case VALID_SXP:
514         regExp = true;
515         break;
516 
517       default:
518         // undocumented return value from RegExpValid!
519         MOZ_ASSERT(false);
520         return NS_ERROR_ILLEGAL_VALUE;
521     }
522 
523     pattern = PL_strdup(aPattern);
524     if (!pattern) return NS_ERROR_OUT_OF_MEMORY;
525   }
526 
527   *aFind = new nsZipFind(this, pattern, regExp);
528   if (!*aFind) {
529     PL_strfree(pattern);
530     return NS_ERROR_OUT_OF_MEMORY;
531   }
532 
533   return NS_OK;
534 }
535 
536 //---------------------------------------------
537 // nsZipFind::FindNext
538 //---------------------------------------------
FindNext(const char ** aResult,uint16_t * aNameLen)539 nsresult nsZipFind::FindNext(const char **aResult, uint16_t *aNameLen) {
540   if (!mArchive || !aResult || !aNameLen) return NS_ERROR_ILLEGAL_VALUE;
541 
542   *aResult = 0;
543   *aNameLen = 0;
544   MOZ_WIN_MEM_TRY_BEGIN
545   // we start from last match, look for next
546   while (mSlot < ZIP_TABSIZE) {
547     // move to next in current chain, or move to new slot
548     mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
549 
550     bool found = false;
551     if (!mItem)
552       ++mSlot;  // no more in this chain, move to next slot
553     else if (!mPattern)
554       found = true;  // always match
555     else if (mRegExp) {
556       char buf[kMaxNameLength + 1];
557       memcpy(buf, mItem->Name(), mItem->nameLength);
558       buf[mItem->nameLength] = '\0';
559       found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
560     } else
561       found = ((mItem->nameLength == strlen(mPattern)) &&
562                (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
563     if (found) {
564       // Need also to return the name length, as it is NOT zero-terminatdd...
565       *aResult = mItem->Name();
566       *aNameLen = mItem->nameLength;
567       return NS_OK;
568     }
569   }
570   MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
571   return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
572 }
573 
574 //***********************************************************
575 //      nsZipArchive  --  private implementation
576 //***********************************************************
577 
578 //---------------------------------------------
579 //  nsZipArchive::CreateZipItem
580 //---------------------------------------------
CreateZipItem()581 nsZipItem *nsZipArchive::CreateZipItem() {
582   // Arena allocate the nsZipItem
583   return (nsZipItem *)mArena.Allocate(sizeof(nsZipItem));
584 }
585 
586 //---------------------------------------------
587 //  nsZipArchive::BuildFileList
588 //---------------------------------------------
BuildFileList(PRFileDesc * aFd)589 nsresult nsZipArchive::BuildFileList(PRFileDesc *aFd) {
590   // Get archive size using end pos
591   const uint8_t *buf;
592   const uint8_t *startp = mFd->mFileData;
593   const uint8_t *endp = startp + mFd->mLen;
594   MOZ_WIN_MEM_TRY_BEGIN
595   uint32_t centralOffset = 4;
596   if (mFd->mLen > ZIPCENTRAL_SIZE &&
597       xtolong(startp + centralOffset) == CENTRALSIG) {
598     // Success means optimized jar layout from bug 559961 is in effect
599     uint32_t readaheadLength = xtolong(startp);
600     if (readaheadLength) {
601 #if defined(XP_SOLARIS)
602       posix_madvise(const_cast<uint8_t *>(startp), readaheadLength,
603                     POSIX_MADV_WILLNEED);
604 #elif defined(XP_UNIX)
605       madvise(const_cast<uint8_t *>(startp), readaheadLength, MADV_WILLNEED);
606 #elif defined(XP_WIN)
607       if (aFd) {
608         HANDLE hFile = (HANDLE)PR_FileDesc2NativeHandle(aFd);
609         mozilla::ReadAhead(hFile, 0, readaheadLength);
610       }
611 #endif
612     }
613   } else {
614     for (buf = endp - ZIPEND_SIZE; buf > startp; buf--) {
615       if (xtolong(buf) == ENDSIG) {
616         centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
617         break;
618       }
619     }
620   }
621 
622   if (!centralOffset) {
623     nsZipArchive::sFileCorruptedReason = "nsZipArchive: no central offset";
624     return NS_ERROR_FILE_CORRUPTED;
625   }
626 
627   buf = startp + centralOffset;
628 
629   // avoid overflow of startp + centralOffset.
630   if (buf < startp) {
631     nsZipArchive::sFileCorruptedReason =
632         "nsZipArchive: overflow looking for central directory";
633     return NS_ERROR_FILE_CORRUPTED;
634   }
635 
636   //-- Read the central directory headers
637   uint32_t sig = 0;
638   while ((buf + int32_t(sizeof(uint32_t)) > buf) &&
639          (buf + int32_t(sizeof(uint32_t)) <= endp) &&
640          ((sig = xtolong(buf)) == CENTRALSIG)) {
641     // Make sure there is enough data available.
642     if ((buf > endp) || (endp - buf < ZIPCENTRAL_SIZE)) {
643       nsZipArchive::sFileCorruptedReason =
644           "nsZipArchive: central directory too small";
645       return NS_ERROR_FILE_CORRUPTED;
646     }
647 
648     // Read the fixed-size data.
649     ZipCentral *central = (ZipCentral *)buf;
650 
651     uint16_t namelen = xtoint(central->filename_len);
652     uint16_t extralen = xtoint(central->extrafield_len);
653     uint16_t commentlen = xtoint(central->commentfield_len);
654     uint32_t diff = ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
655 
656     // Sanity check variable sizes and refuse to deal with
657     // anything too big: it's likely a corrupt archive.
658     if (namelen < 1 || namelen > kMaxNameLength) {
659       nsZipArchive::sFileCorruptedReason = "nsZipArchive: namelen out of range";
660       return NS_ERROR_FILE_CORRUPTED;
661     }
662     if (buf >= buf + diff ||  // No overflow
663         buf >= endp - diff) {
664       nsZipArchive::sFileCorruptedReason =
665           "nsZipArchive: overflow looking for next item";
666       return NS_ERROR_FILE_CORRUPTED;
667     }
668 
669     // Point to the next item at the top of loop
670     buf += diff;
671 
672     nsZipItem *item = CreateZipItem();
673     if (!item) return NS_ERROR_OUT_OF_MEMORY;
674 
675     item->central = central;
676     item->nameLength = namelen;
677     item->isSynthetic = false;
678 
679     // Add item to file table
680     uint32_t hash = HashName(item->Name(), namelen);
681     item->next = mFiles[hash];
682     mFiles[hash] = item;
683 
684     sig = 0;
685   } /* while reading central directory records */
686 
687   if (sig != ENDSIG) {
688     nsZipArchive::sFileCorruptedReason = "nsZipArchive: unexpected sig";
689     return NS_ERROR_FILE_CORRUPTED;
690   }
691 
692   // Make the comment available for consumers.
693   if ((endp >= buf) && (endp - buf >= ZIPEND_SIZE)) {
694     ZipEnd *zipend = (ZipEnd *)buf;
695 
696     buf += ZIPEND_SIZE;
697     uint16_t commentlen = xtoint(zipend->commentfield_len);
698     if (endp - buf >= commentlen) {
699       mCommentPtr = (const char *)buf;
700       mCommentLen = commentlen;
701     }
702   }
703 
704   MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
705   return NS_OK;
706 }
707 
708 //---------------------------------------------
709 //  nsZipArchive::BuildSynthetics
710 //---------------------------------------------
BuildSynthetics()711 nsresult nsZipArchive::BuildSynthetics() {
712   if (mBuiltSynthetics) return NS_OK;
713   mBuiltSynthetics = true;
714 
715   MOZ_WIN_MEM_TRY_BEGIN
716   // Create synthetic entries for any missing directories.
717   // Do this when all ziptable has scanned to prevent double entries.
718   for (auto *item : mFiles) {
719     for (; item != nullptr; item = item->next) {
720       if (item->isSynthetic) continue;
721 
722       //-- add entries for directories in the current item's path
723       //-- go from end to beginning, because then we can stop trying
724       //-- to create diritems if we find that the diritem we want to
725       //-- create already exists
726       //-- start just before the last char so as to not add the item
727       //-- twice if it's a directory
728       uint16_t namelen = item->nameLength;
729       MOZ_ASSERT(namelen > 0,
730                  "Attempt to build synthetic for zero-length entry name!");
731       const char *name = item->Name();
732       for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--) {
733         if (name[dirlen - 1] != '/') continue;
734 
735         // The character before this is '/', so if this is also '/' then we
736         // have an empty path component. Skip it.
737         if (name[dirlen] == '/') continue;
738 
739         // Is the directory already in the file table?
740         uint32_t hash = HashName(item->Name(), dirlen);
741         bool found = false;
742         for (nsZipItem *zi = mFiles[hash]; zi != nullptr; zi = zi->next) {
743           if ((dirlen == zi->nameLength) &&
744               (0 == memcmp(item->Name(), zi->Name(), dirlen))) {
745             // we've already added this dir and all its parents
746             found = true;
747             break;
748           }
749         }
750         // if the directory was found, break out of the directory
751         // creation loop now that we know all implicit directories
752         // are there -- otherwise, start creating the zip item
753         if (found) break;
754 
755         nsZipItem *diritem = CreateZipItem();
756         if (!diritem) return NS_ERROR_OUT_OF_MEMORY;
757 
758         // Point to the central record of the original item for the name part.
759         diritem->central = item->central;
760         diritem->nameLength = dirlen;
761         diritem->isSynthetic = true;
762 
763         // add diritem to the file table
764         diritem->next = mFiles[hash];
765         mFiles[hash] = diritem;
766       } /* end processing of dirs in item's name */
767     }
768   }
769   MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
770   return NS_OK;
771 }
772 
GetFD()773 nsZipHandle *nsZipArchive::GetFD() {
774   if (!mFd) return nullptr;
775   return mFd.get();
776 }
777 
778 //---------------------------------------------
779 // nsZipArchive::GetDataOffset
780 //---------------------------------------------
GetDataOffset(nsZipItem * aItem)781 uint32_t nsZipArchive::GetDataOffset(nsZipItem *aItem) {
782   MOZ_ASSERT(aItem);
783   MOZ_WIN_MEM_TRY_BEGIN
784   //-- read local header to get variable length values and calculate
785   //-- the real data offset
786   uint32_t len = mFd->mLen;
787   const uint8_t *data = mFd->mFileData;
788   uint32_t offset = aItem->LocalOffset();
789   if (len < ZIPLOCAL_SIZE || offset > len - ZIPLOCAL_SIZE) return 0;
790 
791   // -- check signature before using the structure, in case the zip file is
792   // corrupt
793   ZipLocal *Local = (ZipLocal *)(data + offset);
794   if ((xtolong(Local->signature) != LOCALSIG)) return 0;
795 
796   //-- NOTE: extralen is different in central header and local header
797   //--       for archives created using the Unix "zip" utility. To set
798   //--       the offset accurately we need the _local_ extralen.
799   offset += ZIPLOCAL_SIZE + xtoint(Local->filename_len) +
800             xtoint(Local->extrafield_len);
801 
802   return offset;
803   MOZ_WIN_MEM_TRY_CATCH(return 0)
804 }
805 
806 //---------------------------------------------
807 // nsZipArchive::GetData
808 //---------------------------------------------
GetData(nsZipItem * aItem)809 const uint8_t *nsZipArchive::GetData(nsZipItem *aItem) {
810   MOZ_ASSERT(aItem);
811   MOZ_WIN_MEM_TRY_BEGIN
812   uint32_t offset = GetDataOffset(aItem);
813 
814   // -- check if there is enough source data in the file
815   if (!offset || mFd->mLen < aItem->Size() ||
816       offset > mFd->mLen - aItem->Size() ||
817       (aItem->Compression() == STORED && aItem->Size() != aItem->RealSize())) {
818     return nullptr;
819   }
820 
821   return mFd->mFileData + offset;
822   MOZ_WIN_MEM_TRY_CATCH(return nullptr)
823 }
824 
825 // nsZipArchive::GetComment
GetComment(nsACString & aComment)826 bool nsZipArchive::GetComment(nsACString &aComment) {
827   MOZ_WIN_MEM_TRY_BEGIN
828   aComment.Assign(mCommentPtr, mCommentLen);
829   MOZ_WIN_MEM_TRY_CATCH(return false)
830   return true;
831 }
832 
833 //---------------------------------------------
834 // nsZipArchive::SizeOfMapping
835 //---------------------------------------------
SizeOfMapping()836 int64_t nsZipArchive::SizeOfMapping() { return mFd ? mFd->SizeOfMapping() : 0; }
837 
838 //------------------------------------------
839 // nsZipArchive constructor and destructor
840 //------------------------------------------
841 
nsZipArchive()842 nsZipArchive::nsZipArchive()
843     : mRefCnt(0),
844       mCommentPtr(nullptr),
845       mCommentLen(0),
846       mBuiltSynthetics(false) {
847   zipLog.AddRef();
848 
849   // initialize the table to nullptr
850   memset(mFiles, 0, sizeof(mFiles));
851 }
852 
853 NS_IMPL_ADDREF(nsZipArchive)
NS_IMPL_RELEASE(nsZipArchive)854 NS_IMPL_RELEASE(nsZipArchive)
855 
856 nsZipArchive::~nsZipArchive() {
857   CloseArchive();
858 
859   zipLog.Release();
860 }
861 
862 //------------------------------------------
863 // nsZipFind constructor and destructor
864 //------------------------------------------
865 
nsZipFind(nsZipArchive * aZip,char * aPattern,bool aRegExp)866 nsZipFind::nsZipFind(nsZipArchive *aZip, char *aPattern, bool aRegExp)
867     : mArchive(aZip),
868       mPattern(aPattern),
869       mItem(nullptr),
870       mSlot(0),
871       mRegExp(aRegExp) {
872   MOZ_COUNT_CTOR(nsZipFind);
873 }
874 
~nsZipFind()875 nsZipFind::~nsZipFind() {
876   PL_strfree(mPattern);
877 
878   MOZ_COUNT_DTOR(nsZipFind);
879 }
880 
881 //------------------------------------------
882 // helper functions
883 //------------------------------------------
884 
885 /*
886  * HashName
887  *
888  * returns a hash key for the entry name
889  */
890 MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
HashName(const char * aName,uint16_t len)891 static uint32_t HashName(const char *aName, uint16_t len) {
892   MOZ_ASSERT(aName != 0);
893 
894   const uint8_t *p = (const uint8_t *)aName;
895   const uint8_t *endp = p + len;
896   uint32_t val = 0;
897   while (p != endp) {
898     val = val * 37 + *p++;
899   }
900 
901   return (val % ZIP_TABSIZE);
902 }
903 
904 /*
905  *  x t o i n t
906  *
907  *  Converts a two byte ugly endianed integer
908  *  to our platform's integer.
909  */
xtoint(const uint8_t * ii)910 static uint16_t xtoint(const uint8_t *ii) {
911   return (uint16_t)((ii[0]) | (ii[1] << 8));
912 }
913 
914 /*
915  *  x t o l o n g
916  *
917  *  Converts a four byte ugly endianed integer
918  *  to our platform's integer.
919  */
xtolong(const uint8_t * ll)920 static uint32_t xtolong(const uint8_t *ll) {
921   return (uint32_t)((ll[0] << 0) | (ll[1] << 8) | (ll[2] << 16) |
922                     (ll[3] << 24));
923 }
924 
925 /*
926  * GetModTime
927  *
928  * returns last modification time in microseconds
929  */
GetModTime(uint16_t aDate,uint16_t aTime)930 static PRTime GetModTime(uint16_t aDate, uint16_t aTime) {
931   // Note that on DST shift we can't handle correctly the hour that is valid
932   // in both DST zones
933   PRExplodedTime time;
934 
935   time.tm_usec = 0;
936 
937   time.tm_hour = (aTime >> 11) & 0x1F;
938   time.tm_min = (aTime >> 5) & 0x3F;
939   time.tm_sec = (aTime & 0x1F) * 2;
940 
941   time.tm_year = (aDate >> 9) + 1980;
942   time.tm_month = ((aDate >> 5) & 0x0F) - 1;
943   time.tm_mday = aDate & 0x1F;
944 
945   time.tm_params.tp_gmt_offset = 0;
946   time.tm_params.tp_dst_offset = 0;
947 
948   PR_NormalizeTime(&time, PR_GMTParameters);
949   time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
950   PR_NormalizeTime(&time, PR_GMTParameters);
951   time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
952 
953   return PR_ImplodeTime(&time);
954 }
955 
nsZipItem()956 nsZipItem::nsZipItem()
957     : next(nullptr), central(nullptr), nameLength(0), isSynthetic(false) {}
958 
LocalOffset()959 uint32_t nsZipItem::LocalOffset() { return xtolong(central->localhdr_offset); }
960 
Size()961 uint32_t nsZipItem::Size() { return isSynthetic ? 0 : xtolong(central->size); }
962 
RealSize()963 uint32_t nsZipItem::RealSize() {
964   return isSynthetic ? 0 : xtolong(central->orglen);
965 }
966 
CRC32()967 uint32_t nsZipItem::CRC32() {
968   return isSynthetic ? 0 : xtolong(central->crc32);
969 }
970 
Date()971 uint16_t nsZipItem::Date() {
972   return isSynthetic ? kSyntheticDate : xtoint(central->date);
973 }
974 
Time()975 uint16_t nsZipItem::Time() {
976   return isSynthetic ? kSyntheticTime : xtoint(central->time);
977 }
978 
Compression()979 uint16_t nsZipItem::Compression() {
980   return isSynthetic ? STORED : xtoint(central->method);
981 }
982 
IsDirectory()983 bool nsZipItem::IsDirectory() {
984   return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
985 }
986 
Mode()987 uint16_t nsZipItem::Mode() {
988   if (isSynthetic) return 0755;
989   return ((uint16_t)(central->external_attributes[2]) | 0x100);
990 }
991 
GetExtraField(uint16_t aTag,uint16_t * aBlockSize)992 const uint8_t *nsZipItem::GetExtraField(uint16_t aTag, uint16_t *aBlockSize) {
993   if (isSynthetic) return nullptr;
994   MOZ_WIN_MEM_TRY_BEGIN
995   const unsigned char *buf =
996       ((const unsigned char *)central) + ZIPCENTRAL_SIZE + nameLength;
997   uint32_t buflen = (uint32_t)xtoint(central->extrafield_len);
998   uint32_t pos = 0;
999   uint16_t tag, blocksize;
1000 
1001   while (buf && (pos + 4) <= buflen) {
1002     tag = xtoint(buf + pos);
1003     blocksize = xtoint(buf + pos + 2);
1004 
1005     if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
1006       *aBlockSize = blocksize;
1007       return buf + pos;
1008     }
1009 
1010     pos += blocksize + 4;
1011   }
1012 
1013   MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1014   return nullptr;
1015 }
1016 
LastModTime()1017 PRTime nsZipItem::LastModTime() {
1018   if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
1019 
1020   // Try to read timestamp from extra field
1021   uint16_t blocksize;
1022   const uint8_t *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
1023   if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
1024     return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
1025   }
1026 
1027   return GetModTime(Date(), Time());
1028 }
1029 
nsZipCursor(nsZipItem * item,nsZipArchive * aZip,uint8_t * aBuf,uint32_t aBufSize,bool doCRC)1030 nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t *aBuf,
1031                          uint32_t aBufSize, bool doCRC)
1032     : mItem(item),
1033       mBuf(aBuf),
1034       mBufSize(aBufSize)
1035 #ifdef MOZ_JAR_BROTLI
1036       ,
1037       mBrotliState(nullptr)
1038 #endif
1039       ,
1040       mCRC(0),
1041       mDoCRC(doCRC) {
1042   if (mItem->Compression() == DEFLATED) {
1043 #ifdef DEBUG
1044     nsresult status =
1045 #endif
1046         gZlibInit(&mZs);
1047     NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
1048     NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
1049   }
1050 
1051   mZs.avail_in = item->Size();
1052   mZs.next_in = (Bytef *)aZip->GetData(item);
1053 
1054 #ifdef MOZ_JAR_BROTLI
1055   if (mItem->Compression() == MOZ_JAR_BROTLI) {
1056     mBrotliState = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
1057   }
1058 #endif
1059 
1060   if (doCRC) mCRC = crc32(0L, Z_NULL, 0);
1061 }
1062 
~nsZipCursor()1063 nsZipCursor::~nsZipCursor() {
1064   if (mItem->Compression() == DEFLATED) {
1065     inflateEnd(&mZs);
1066   }
1067 #ifdef MOZ_JAR_BROTLI
1068   if (mItem->Compression() == MOZ_JAR_BROTLI) {
1069     BrotliDecoderDestroyInstance(mBrotliState);
1070   }
1071 #endif
1072 }
1073 
ReadOrCopy(uint32_t * aBytesRead,bool aCopy)1074 uint8_t *nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) {
1075   int zerr;
1076   uint8_t *buf = nullptr;
1077   bool verifyCRC = true;
1078 
1079   if (!mZs.next_in) return nullptr;
1080   MOZ_WIN_MEM_TRY_BEGIN
1081   switch (mItem->Compression()) {
1082     case STORED:
1083       if (!aCopy) {
1084         *aBytesRead = mZs.avail_in;
1085         buf = mZs.next_in;
1086         mZs.next_in += mZs.avail_in;
1087         mZs.avail_in = 0;
1088       } else {
1089         *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
1090         memcpy(mBuf, mZs.next_in, *aBytesRead);
1091         mZs.avail_in -= *aBytesRead;
1092         mZs.next_in += *aBytesRead;
1093       }
1094       break;
1095     case DEFLATED:
1096       buf = mBuf;
1097       mZs.next_out = buf;
1098       mZs.avail_out = mBufSize;
1099 
1100       zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
1101       if (zerr != Z_OK && zerr != Z_STREAM_END) return nullptr;
1102 
1103       *aBytesRead = mZs.next_out - buf;
1104       verifyCRC = (zerr == Z_STREAM_END);
1105       break;
1106 #ifdef MOZ_JAR_BROTLI
1107     case MOZ_JAR_BROTLI: {
1108       buf = mBuf;
1109       mZs.next_out = buf;
1110       /* The brotli library wants size_t, but z_stream only contains
1111        * unsigned int for avail_*. So use temporary stack values. */
1112       size_t avail_out = mBufSize;
1113       size_t avail_in = mZs.avail_in;
1114       BrotliDecoderResult result = BrotliDecoderDecompressStream(
1115           mBrotliState, &avail_in,
1116           const_cast<const unsigned char **>(&mZs.next_in), &avail_out,
1117           &mZs.next_out, nullptr);
1118       /* We don't need to update avail_out, it's not used outside this
1119        * function. */
1120       mZs.avail_in = avail_in;
1121 
1122       if (result == BROTLI_DECODER_RESULT_ERROR) {
1123         return nullptr;
1124       }
1125 
1126       *aBytesRead = mZs.next_out - buf;
1127       verifyCRC = (result == BROTLI_DECODER_RESULT_SUCCESS);
1128       break;
1129     }
1130 #endif
1131     default:
1132       return nullptr;
1133   }
1134 
1135   if (mDoCRC) {
1136     mCRC = crc32(mCRC, (const unsigned char *)buf, *aBytesRead);
1137     if (verifyCRC && mCRC != mItem->CRC32()) return nullptr;
1138   }
1139   MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1140   return buf;
1141 }
1142 
nsZipItemPtr_base(nsZipArchive * aZip,const char * aEntryName,bool doCRC)1143 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char *aEntryName,
1144                                      bool doCRC)
1145     : mReturnBuf(nullptr), mReadlen(0) {
1146   // make sure the ziparchive hangs around
1147   mZipHandle = aZip->GetFD();
1148 
1149   nsZipItem *item = aZip->GetItem(aEntryName);
1150   if (!item) return;
1151 
1152   uint32_t size = 0;
1153   bool compressed = (item->Compression() == DEFLATED);
1154 #ifdef MOZ_JAR_BROTLI
1155   compressed |= (item->Compression() == MOZ_JAR_BROTLI);
1156 #endif
1157   if (compressed) {
1158     size = item->RealSize();
1159     mAutoBuf = MakeUniqueFallible<uint8_t[]>(size);
1160     if (!mAutoBuf) {
1161       return;
1162     }
1163   }
1164 
1165   nsZipCursor cursor(item, aZip, mAutoBuf.get(), size, doCRC);
1166   mReturnBuf = cursor.Read(&mReadlen);
1167   if (!mReturnBuf) {
1168     return;
1169   }
1170 
1171   if (mReadlen != item->RealSize()) {
1172     NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
1173     mReturnBuf = nullptr;
1174     return;
1175   }
1176 }
1177 
1178 /* static */ const char *nsZipArchive::sFileCorruptedReason = nullptr;
1179