Bug 1391268 - 1. Add call to verify CRC; r?glandium draft
authorJim Chen <nchen@mozilla.com>
Wed, 31 Jan 2018 15:34:17 -0500
changeset 749615 2d18a06806fdbf37822fe1450719175ad91c5813
parent 749555 205707b678d24ba268b97668b78dc605f701a5e0
child 749616 20f0a95273efb12bd58c36a11691e0890e53c03a
push id97462
push userbmo:nchen@mozilla.com
push dateWed, 31 Jan 2018 20:35:46 +0000
reviewersglandium
bugs1391268
milestone60.0a1
Bug 1391268 - 1. Add call to verify CRC; r?glandium To reliably detect corrupt APK, this patch adds a GeckoLoader.verifyCRC call to enable verification of CRC before extracting libs. MozReview-Commit-ID: 5EpIfwREGIv
mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
mozglue/android/nsGeckoUtils.cpp
mozglue/linker/Zip.cpp
mozglue/linker/Zip.h
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
@@ -486,16 +486,17 @@ public final class GeckoLoader {
             thread.getUncaughtExceptionHandler();
         if (uncaughtHandler != null) {
             uncaughtHandler.uncaughtException(thread, new AbortException(msg));
         }
     }
 
     // These methods are implemented in mozglue/android/nsGeckoUtils.cpp
     private static native void putenv(String map);
+    public static native boolean verifyCRC(String apkName);
 
     // These methods are implemented in mozglue/android/APKOpen.cpp
     public static native void nativeRun(String[] args, int crashFd, int ipcFd);
     private static native void loadGeckoLibsNative(String apkName);
     private static native void loadSQLiteLibsNative(String apkName);
     private static native void loadNSSLibsNative(String apkName);
     public static native boolean neonCompatible();
     public static native void suppressCrashDialog();
--- a/mozglue/android/nsGeckoUtils.cpp
+++ b/mozglue/android/nsGeckoUtils.cpp
@@ -21,16 +21,32 @@ Java_org_mozilla_gecko_mozglue_GeckoLoad
     // better to do here
     str = jenv->GetStringUTFChars(map, nullptr);
     if (str == nullptr)
         return;
     putenv(strdup(str));
     jenv->ReleaseStringUTFChars(map, str);
 }
 
+extern "C" APKOPEN_EXPORT jboolean MOZ_JNICALL
+Java_org_mozilla_gecko_mozglue_GeckoLoader_verifyCRC(JNIEnv *jenv, jclass, jstring jApkName) {
+  const char* str;
+  // XXX: java doesn't give us true UTF8, we should figure out something
+  // better to do here
+  str = jenv->GetStringUTFChars(jApkName, nullptr);
+  if (str == nullptr) {
+    return false;
+  }
+
+  RefPtr<Zip> zip = Zip::Create(str);
+  const bool valid = zip->VerifyCRC();
+  jenv->ReleaseStringUTFChars(jApkName, str);
+  return jboolean(valid);
+}
+
 extern "C"
 __attribute__ ((visibility("default")))
 jobject MOZ_JNICALL
 Java_org_mozilla_gecko_mozglue_DirectBufferAllocator_nativeAllocateDirectBuffer(JNIEnv *jenv, jclass, jlong size)
 {
     jobject buffer = nullptr;
     void* mem = malloc(size);
     if (mem) {
--- a/mozglue/linker/Zip.cpp
+++ b/mozglue/linker/Zip.cpp
@@ -179,16 +179,76 @@ Zip::GetFirstEntry() const
   entries = DirectoryEntry::validate(static_cast<const char *>(mapped)
                                  + end->offset);
   if (!entries) {
     ERROR("%s - Couldn't find central directory record", name);
   }
   return entries;
 }
 
+bool
+Zip::VerifyCRC() const
+{
+  AutoLock lock(&mutex);
+
+  for (const DirectoryEntry *entry = GetFirstEntry();
+       entry; entry = entry->GetNext()) {
+    const LocalFile *file = LocalFile::validate(
+        static_cast<const char *>(mapped) + entry->offset);
+    uint32_t crc = crc32(0, nullptr, 0);
+
+    DEBUG_LOG("%.*s: crc=%08x", int(entry->filenameSize),
+              reinterpret_cast<const char *>(entry) + sizeof(*entry),
+              uint32_t(entry->CRC32));
+
+    if (entry->compression == Stream::Type::STORE) {
+      crc = crc32(crc, static_cast<const uint8_t*>(file->GetData()),
+                  entry->compressedSize);
+      DEBUG_LOG(" STORE size=%d crc=%08x", int(entry->compressedSize), crc);
+
+    } else if (entry->compression == Stream::Type::DEFLATE) {
+      zxx_stream zstream;
+      Bytef buffer[1024];
+      zstream.avail_in = entry->compressedSize;
+      zstream.next_in = reinterpret_cast<Bytef *>(
+                        const_cast<void *>(file->GetData()));
+
+      if (inflateInit2(&zstream, -MAX_WBITS) != Z_OK) {
+        return false;
+      }
+
+      for (;;) {
+        zstream.avail_out = sizeof(buffer);
+        zstream.next_out = buffer;
+
+        int ret = inflate(&zstream, Z_SYNC_FLUSH);
+        crc = crc32(crc, buffer, sizeof(buffer) - zstream.avail_out);
+
+        if (ret == Z_STREAM_END) {
+          break;
+        } else if (ret != Z_OK) {
+          return false;
+        }
+      }
+
+      inflateEnd(&zstream);
+      DEBUG_LOG(" DEFLATE size=%d crc=%08x", int(zstream.total_out), crc);
+
+    } else {
+      continue;
+    }
+
+    if (entry->CRC32 != crc) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 ZipCollection ZipCollection::Singleton;
 
 static pthread_mutex_t sZipCollectionMutex = PTHREAD_MUTEX_INITIALIZER;
 
 already_AddRefed<Zip>
 ZipCollection::GetZip(const char *path)
 {
   {
--- a/mozglue/linker/Zip.h
+++ b/mozglue/linker/Zip.h
@@ -258,16 +258,21 @@ public:
   /**
    * Returns the file name of the archive
    */
   const char *GetName() const
   {
     return name;
   }
 
+  /**
+   * Returns whether all files have correct CRC checksum.
+   */
+  bool VerifyCRC() const;
+
 private:
   /* File name of the archive */
   char *name;
   /* Address where the Zip archive is mapped */
   void *mapped;
   /* Size of the archive */
   size_t size;