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