Bug 1225004 - Record reason for NS_ERROR_FILE_CORRUPTED in nsLayoutStylesheetCache::LoadSheet in crash reports. r=roc, a=lizzard
authorCameron McCormack <cam@mcc.id.au>
Mon, 16 Nov 2015 19:18:45 +1100
changeset 303765 2a339d6b372566faedde8fdc11d7a47d8266be2a
parent 303764 0141686cbdc0c9eea34959de7016375873412864
child 303766 f0b7aa7305fffc9d0bd76d716892bcacf173fb0b
push id5392
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:08:23 +0000
treeherdermozilla-beta@16ce8562a975 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, lizzard
bugs1225004
milestone44.0a2
Bug 1225004 - Record reason for NS_ERROR_FILE_CORRUPTED in nsLayoutStylesheetCache::LoadSheet in crash reports. r=roc, a=lizzard
layout/style/nsLayoutStylesheetCache.cpp
modules/libjar/nsJAR.cpp
modules/libjar/nsJARInputStream.cpp
modules/libjar/nsZipArchive.cpp
modules/libjar/nsZipArchive.h
xpcom/build/FileLocation.cpp
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -22,16 +22,18 @@
 #ifdef MOZ_CRASHREPORTER
 #include "mozilla/Omnijar.h"
 #include "nsDirectoryService.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsExceptionHandler.h"
 #include "nsIChromeRegistry.h"
 #include "nsISimpleEnumerator.h"
 #include "nsISubstitutingProtocolHandler.h"
+#include "zlib.h"
+#include "nsZipArchive.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::css;
 
 static bool sNumberControlEnabled;
 
 #define NUMBER_CONTROL_PREF "dom.forms.number"
@@ -464,16 +466,40 @@ nsLayoutStylesheetCache::LoadSheetFile(n
 
   nsCOMPtr<nsIURI> uri;
   NS_NewFileURI(getter_AddRefs(uri), aFile);
 
   LoadSheet(uri, aSheet, aParsingMode);
 }
 
 #ifdef MOZ_CRASHREPORTER
+static inline nsresult
+ComputeCRC32(nsIFile* aFile, uint32_t* aResult)
+{
+  PRFileDesc* fd;
+  nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t crc = crc32(0, nullptr, 0);
+
+  unsigned char buf[512];
+  int32_t n;
+  while ((n = PR_Read(fd, buf, sizeof(buf))) > 0) {
+    crc = crc32(crc, buf, n);
+  }
+  PR_Close(fd);
+
+  if (n < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResult = crc;
+  return NS_OK;
+}
+
 static void
 ListInterestingFiles(nsString& aAnnotation, nsIFile* aFile,
                      const nsTArray<nsString>& aInterestingFilenames)
 {
   nsString filename;
   aFile->GetLeafName(filename);
   for (const nsString& interestingFilename : aInterestingFilenames) {
     if (interestingFilename == filename) {
@@ -483,17 +509,24 @@ ListInterestingFiles(nsString& aAnnotati
       aAnnotation.Append(path);
       aAnnotation.AppendLiteral(" (");
       int64_t size;
       if (NS_SUCCEEDED(aFile->GetFileSize(&size))) {
         aAnnotation.AppendPrintf("%ld", size);
       } else {
         aAnnotation.AppendLiteral("???");
       }
-      aAnnotation.AppendLiteral(" bytes)\n");
+      aAnnotation.AppendLiteral(" bytes, crc32 = ");
+      uint32_t crc;
+      nsresult rv = ComputeCRC32(aFile, &crc);
+      if (NS_SUCCEEDED(rv)) {
+        aAnnotation.AppendPrintf("0x%08x)\n", crc);
+      } else {
+        aAnnotation.AppendPrintf("error 0x%08x)\n", uint32_t(rv));
+      }
       return;
     }
   }
 
   bool isDir = false;
   aFile->IsDirectory(&isDir);
 
   if (!isDir) {
@@ -548,16 +581,24 @@ AnnotateCrashReport(nsIURI* aURI)
 
   nsString annotation;
 
   // The URL of the sheet that failed to load.
   annotation.AppendLiteral("Error loading sheet: ");
   annotation.Append(NS_ConvertUTF8toUTF16(spec).get());
   annotation.Append('\n');
 
+  annotation.AppendLiteral("NS_ERROR_FILE_CORRUPTION reason: ");
+  if (nsZipArchive::sFileCorruptedReason) {
+    annotation.Append(NS_ConvertUTF8toUTF16(nsZipArchive::sFileCorruptedReason).get());
+    annotation.Append('\n');
+  } else {
+    annotation.AppendLiteral("(none)\n");
+  }
+
   // The jar: or file: URL that the sheet's resource: or chrome: URL
   // resolves to.
   if (scheme.EqualsLiteral("resource")) {
     annotation.AppendLiteral("Real location: ");
     nsCOMPtr<nsISubstitutingProtocolHandler> handler;
     nsCOMPtr<nsIIOService> io(do_GetIOService());
     if (io) {
       nsCOMPtr<nsIProtocolHandler> ph;
@@ -734,16 +775,19 @@ nsLayoutStylesheetCache::LoadSheet(nsIUR
     NS_IF_ADDREF(gCSSLoader);
     if (!gCSSLoader) {
       ErrorLoadingBuiltinSheet(aURI, "no Loader");
       return;
     }
   }
 
 
+#ifdef MOZ_CRASHREPORTER
+  nsZipArchive::sFileCorruptedReason = nullptr;
+#endif
   nsresult rv = gCSSLoader->LoadSheetSync(aURI, aParsingMode, true,
                                           getter_AddRefs(aSheet));
   if (NS_FAILED(rv)) {
     ErrorLoadingBuiltinSheet(aURI,
       nsPrintfCString("LoadSheetSync failed with error %x", rv).get());
   }
 }
 
--- a/modules/libjar/nsJAR.cpp
+++ b/modules/libjar/nsJAR.cpp
@@ -441,24 +441,29 @@ nsJAR::LoadEntry(const nsACString &aFile
   rv = GetInputStream(aFilename, getter_AddRefs(manifestStream));
   if (NS_FAILED(rv)) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
 
   //-- Read the manifest file into memory
   char* buf;
   uint64_t len64;
   rv = manifestStream->Available(&len64);
   if (NS_FAILED(rv)) return rv;
-  NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_CORRUPTED); // bug 164695
+  if (len64 >= UINT32_MAX) { // bug 164695
+    nsZipArchive::sFileCorruptedReason = "nsJAR: invalid manifest size";
+    return NS_ERROR_FILE_CORRUPTED;
+  }
   uint32_t len = (uint32_t)len64;
   buf = (char*)malloc(len+1);
   if (!buf) return NS_ERROR_OUT_OF_MEMORY;
   uint32_t bytesRead;
   rv = manifestStream->Read(buf, len, &bytesRead);
-  if (bytesRead != len)
+  if (bytesRead != len) {
+    nsZipArchive::sFileCorruptedReason = "nsJAR: manifest too small";
     rv = NS_ERROR_FILE_CORRUPTED;
+  }
   if (NS_FAILED(rv)) {
     free(buf);
     return rv;
   }
   buf[len] = '\0'; //Null-terminate the buffer
   *aBuf = buf;
   if (aBufLen)
     *aBufLen = len;
@@ -534,16 +539,17 @@ nsJAR::ParseManifest()
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Check if there is more than one manifest, if so then error!
   rv = files->HasMore(&more);
   if (NS_FAILED(rv)) return rv;
   if (more)
   {
     mParsedManifest = true;
+    nsZipArchive::sFileCorruptedReason = "nsJAR: duplicate manifests";
     return NS_ERROR_FILE_CORRUPTED; // More than one MF file
   }
 
   nsXPIDLCString manifestBuffer;
   uint32_t manifestLen;
   rv = LoadEntry(manifestFilename, getter_Copies(manifestBuffer), &manifestLen);
   if (NS_FAILED(rv)) return rv;
 
@@ -633,18 +639,20 @@ nsJAR::ParseOneFile(const char* filebuf,
   //-- Check file header
   const char* nextLineStart = filebuf;
   nsAutoCString curLine;
   int32_t linelen;
   linelen = ReadLine(&nextLineStart);
   curLine.Assign(filebuf, linelen);
 
   if ( ((aFileType == JAR_MF) && !curLine.Equals(JAR_MF_HEADER) ) ||
-       ((aFileType == JAR_SF) && !curLine.Equals(JAR_SF_HEADER) ) )
+       ((aFileType == JAR_SF) && !curLine.Equals(JAR_SF_HEADER) ) ) {
+     nsZipArchive::sFileCorruptedReason = "nsJAR: invalid manifest header";
      return NS_ERROR_FILE_CORRUPTED;
+  }
 
   //-- Skip header section
   do {
     linelen = ReadLine(&nextLineStart);
   } while (linelen > 0);
 
   //-- Set up parsing variables
   const char* curPos;
--- a/modules/libjar/nsJARInputStream.cpp
+++ b/modules/libjar/nsJARInputStream.cpp
@@ -53,18 +53,20 @@ nsJARInputStream::InitFile(nsJAR *aJar, 
 
        default:
            return NS_ERROR_NOT_IMPLEMENTED;
     }
    
     // Must keep handle to filepointer and mmap structure as long as we need access to the mmapped data
     mFd = aJar->mZip->GetFD();
     mZs.next_in = (Bytef *)aJar->mZip->GetData(item);
-    if (!mZs.next_in)
+    if (!mZs.next_in) {
+        nsZipArchive::sFileCorruptedReason = "nsJARInputStream: !mZs.next_in";
         return NS_ERROR_FILE_CORRUPTED;
+    }
     mZs.avail_in = item->Size();
     mOutSize = item->RealSize();
     mZs.total_out = 0;
     return NS_OK;
 }
 
 nsresult
 nsJARInputStream::InitDirectory(nsJAR* aJar,
@@ -261,32 +263,36 @@ nsJARInputStream::ContinueInflate(char* 
     const uint32_t oldTotalOut = mZs.total_out;
     
     // make sure we aren't reading too much
     mZs.avail_out = std::min(aCount, (mOutSize-oldTotalOut));
     mZs.next_out = (unsigned char*)aBuffer;
 
     // now inflate
     int zerr = inflate(&mZs, Z_SYNC_FLUSH);
-    if ((zerr != Z_OK) && (zerr != Z_STREAM_END))
+    if ((zerr != Z_OK) && (zerr != Z_STREAM_END)) {
+        nsZipArchive::sFileCorruptedReason = "nsJARInputStream: error while inflating";
         return NS_ERROR_FILE_CORRUPTED;
+    }
 
     *aBytesRead = (mZs.total_out - oldTotalOut);
 
     // Calculate the CRC on the output
     mOutCrc = crc32(mOutCrc, (unsigned char*)aBuffer, *aBytesRead);
 
     // be aggressive about ending the inflation
     // for some reason we don't always get Z_STREAM_END
     if (zerr == Z_STREAM_END || mZs.total_out == mOutSize) {
         inflateEnd(&mZs);
 
         // stop returning valid data as soon as we know we have a bad CRC
-        if (mOutCrc != mInCrc)
+        if (mOutCrc != mInCrc) {
+            nsZipArchive::sFileCorruptedReason = "nsJARInputStream: crc mismatch";
             return NS_ERROR_FILE_CORRUPTED;
+        }
     }
 
     return NS_OK;
 }
 
 nsresult
 nsJARInputStream::ReadDirectory(char* aBuffer, uint32_t aCount, uint32_t *aBytesRead)
 {
--- a/modules/libjar/nsZipArchive.cpp
+++ b/modules/libjar/nsZipArchive.cpp
@@ -438,16 +438,17 @@ nsresult nsZipArchive::ExtractFile(nsZip
   nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
 
   nsresult rv = NS_OK;
 
   while (true) {
     uint32_t count = 0;
     uint8_t* buf = cursor.Read(&count);
     if (!buf) {
+      nsZipArchive::sFileCorruptedReason = "nsZipArchive: Read() failed to return a buffer";
       rv = NS_ERROR_FILE_CORRUPTED;
       break;
     } else if (count == 0) {
       break;
     }
 
     if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
       rv = NS_ERROR_FILE_DISK_FULL;
@@ -638,47 +639,57 @@ MOZ_WIN_MEM_TRY_BEGIN
       {
         if (xtolong(buf) == ENDSIG) {
           centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
           break;
         }
       }
   }
 
-  if (!centralOffset)
+  if (!centralOffset) {
+    nsZipArchive::sFileCorruptedReason = "nsZipArchive: no central offset";
     return NS_ERROR_FILE_CORRUPTED;
+  }
 
   buf = startp + centralOffset;
 
   // avoid overflow of startp + centralOffset.
-  if (buf < startp)
+  if (buf < startp) {
+    nsZipArchive::sFileCorruptedReason = "nsZipArchive: overflow looking for central directory";
     return NS_ERROR_FILE_CORRUPTED;
+  }
 
   //-- Read the central directory headers
   uint32_t sig = 0;
   while (buf + int32_t(sizeof(uint32_t)) <= endp &&
          (sig = xtolong(buf)) == CENTRALSIG) {
     // Make sure there is enough data available.
-    if (endp - buf < ZIPCENTRAL_SIZE)
+    if (endp - buf < ZIPCENTRAL_SIZE) {
+      nsZipArchive::sFileCorruptedReason = "nsZipArchive: central directory too small";
       return NS_ERROR_FILE_CORRUPTED;
+    }
 
     // Read the fixed-size data.
     ZipCentral* central = (ZipCentral*)buf;
 
     uint16_t namelen = xtoint(central->filename_len);
     uint16_t extralen = xtoint(central->extrafield_len);
     uint16_t commentlen = xtoint(central->commentfield_len);
     uint32_t diff = ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
 
     // Sanity check variable sizes and refuse to deal with
     // anything too big: it's likely a corrupt archive.
     if (namelen < 1 ||
-        namelen > kMaxNameLength ||
-        buf >= buf + diff || // No overflow
+        namelen > kMaxNameLength) {
+      nsZipArchive::sFileCorruptedReason = "nsZipArchive: namelen out of range";
+      return NS_ERROR_FILE_CORRUPTED;
+    }
+    if (buf >= buf + diff || // No overflow
         buf >= endp - diff) {
+      nsZipArchive::sFileCorruptedReason = "nsZipArchive: overflow looking for next item";
       return NS_ERROR_FILE_CORRUPTED;
     }
 
     // Point to the next item at the top of loop
     buf += diff;
 
     nsZipItem* item = CreateZipItem();
     if (!item)
@@ -691,18 +702,20 @@ MOZ_WIN_MEM_TRY_BEGIN
     // Add item to file table
     uint32_t hash = HashName(item->Name(), namelen);
     item->next = mFiles[hash];
     mFiles[hash] = item;
 
     sig = 0;
   } /* while reading central directory records */
 
-  if (sig != ENDSIG)
+  if (sig != ENDSIG) {
+    nsZipArchive::sFileCorruptedReason = "nsZipArchive: unexpected sig";
     return NS_ERROR_FILE_CORRUPTED;
+  }
 
   // Make the comment available for consumers.
   if (endp - buf >= ZIPEND_SIZE) {
     ZipEnd *zipend = (ZipEnd *)buf;
 
     buf += ZIPEND_SIZE;
     uint16_t commentlen = xtoint(zipend->commentfield_len);
     if (endp - buf >= commentlen) {
@@ -1207,8 +1220,11 @@ nsZipItemPtr_base::nsZipItemPtr_base(nsZ
   }
 
   if (mReadlen != item->RealSize()) {
     NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
     mReturnBuf = nullptr;
     return;
   }
 }
+
+/* static */ const char*
+nsZipArchive::sFileCorruptedReason = nullptr;
--- a/modules/libjar/nsZipArchive.h
+++ b/modules/libjar/nsZipArchive.h
@@ -96,16 +96,18 @@ class nsZipHandle;
 class nsZipArchive final
 {
   friend class nsZipFind;
 
   /** destructing the object closes the archive */
   ~nsZipArchive();
 
 public:
+  static const char* sFileCorruptedReason;
+
   /** constructing does not open the archive. See OpenArchive() */
   nsZipArchive();
 
   /** 
    * OpenArchive 
    * 
    * It's an error to call this more than once on the same nsZipArchive
    * object. If we were allowed to use exceptions this would have been 
--- a/xpcom/build/FileLocation.cpp
+++ b/xpcom/build/FileLocation.cpp
@@ -228,15 +228,19 @@ FileLocation::Data::Copy(char* aBuf, uin
     return NS_OK;
   }
 #if !defined(MOZILLA_XPCOMRT_API)
   else if (mItem) {
     nsZipCursor cursor(mItem, mZip, reinterpret_cast<uint8_t*>(aBuf),
                        aLen, true);
     uint32_t readLen;
     cursor.Copy(&readLen);
-    return (readLen == aLen) ? NS_OK : NS_ERROR_FILE_CORRUPTED;
+    if (readLen != aLen) {
+      nsZipArchive::sFileCorruptedReason = "FileLocation::Data: insufficient data";
+      return NS_ERROR_FILE_CORRUPTED;
+    }
+    return NS_OK;
   }
 #endif // !defined(MOZILLA_XPCOMRT_API)
   return NS_ERROR_NOT_INITIALIZED;
 }
 
 } /* namespace mozilla */