Added additional DEX checksum.

We have a checksum on the base DEX data, but not on the stuff that
dexopt appends.  If a flash block goes "funny" we might not be able to
detect the problem.  This change adds a checksum field to the
"optimized" header.

The new checksum is verified under the same circumstances as the base
DEX checksum: when you use "dexdump", and when you enable additional
checking with -Xcheckdexsum (or the property dalvik.vm.check-dex-sum
is set to "true").

For bug 2255640.
diff --git a/libdex/DexFile.c b/libdex/DexFile.c
index 99b38c9..b139746 100644
--- a/libdex/DexFile.c
+++ b/libdex/DexFile.c
@@ -33,6 +33,10 @@
 #include <fcntl.h>
 #include <errno.h>
 
+// fwd
+static u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader);
+
+
 /*
  * Verifying checksums is good, but it slows things down and causes us to
  * touch every page.  In the "optimized" world, it doesn't work at all,
@@ -753,8 +757,8 @@
     }
 
     /*
-     * Verify the checksum.  This is reasonably quick, but does require
-     * touching every byte in the DEX file.  The checksum changes after
+     * Verify the checksum(s).  This is reasonably quick, but does require
+     * touching every byte in the DEX file.  The base checksum changes after
      * byte-swapping and DEX optimization.
      */
     if (flags & kDexParseVerifyChecksum) {
@@ -767,14 +771,26 @@
         } else {
             LOGV("+++ adler32 checksum (%08x) verified\n", adler);
         }
+
+        const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+        if (pOptHeader != NULL) {
+            adler = dexComputeOptChecksum(pOptHeader);
+            if (adler != pOptHeader->checksum) {
+                LOGE("ERROR: bad opt checksum (%08x vs %08x)\n",
+                    adler, pOptHeader->checksum);
+                if (!(flags & kDexParseContinueOnError))
+                    goto bail;
+            } else {
+                LOGV("+++ adler32 opt checksum (%08x) verified\n", adler);
+            }
+        }
     }
 
     /*
      * Verify the SHA-1 digest.  (Normally we don't want to do this --
-     * the digest is used to uniquely identify a DEX file, and can't be
-     * computed post-optimization.)
-     *
-     * The digest will be invalid after byte swapping and DEX optimization.
+     * the digest is used to uniquely identify the original DEX file, and
+     * can't be computed for verification after the DEX is byte-swapped
+     * and optimized.)
      */
     if (kVerifySignature) {
         unsigned char sha1Digest[kSHA1DigestLen];
@@ -887,6 +903,20 @@
     return (u4) adler32(adler, start + nonSum, pHeader->fileSize - nonSum);
 }
 
+/*
+ * Compute the checksum on the data appended to the DEX file by dexopt.
+ */
+static u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader)
+{
+    const u1* start = (const u1*) pOptHeader + pOptHeader->depsOffset;
+    const u1* end = (const u1*) pOptHeader +
+        pOptHeader->auxOffset + pOptHeader->auxLength;
+
+    uLong adler = adler32(0L, Z_NULL, 0);
+
+    return (u4) adler32(adler, start, end - start);
+}
+
 
 /*
  * Compute the size, in bytes, of a DexCode.
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
index 4b5fe7c..a10aaf5 100644
--- a/libdex/DexFile.h
+++ b/libdex/DexFile.h
@@ -52,7 +52,7 @@
 
 /* same, but for optimized DEX header */
 #define DEX_OPT_MAGIC   "dey\n"
-#define DEX_OPT_MAGIC_VERS  "035\0"
+#define DEX_OPT_MAGIC_VERS  "036\0"
 
 #define DEX_DEP_MAGIC   "deps"
 
@@ -484,8 +484,9 @@
     u4  auxLength;
 
     u4  flags;              /* some info flags */
+    u4  checksum;           /* adler32 checksum covering deps/aux */
 
-    u4  padding;            /* induce 64-bit alignment */
+    /* pad for 64-bit alignment if necessary */
 } DexOptHeader;
 
 #define DEX_FLAG_VERIFIED           (1)     /* tried to verify all classes */
diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c
index 369d707..a5b8b6f 100644
--- a/vm/analysis/DexOptimize.c
+++ b/vm/analysis/DexOptimize.c
@@ -54,6 +54,7 @@
     const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder);
 static void logFailedWrite(size_t expected, ssize_t actual, const char* msg,
     int err);
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum);
 
 static bool rewriteDex(u1* addr, int len, bool doVerify, bool doOpt,\
     u4* pHeaderFlags, DexClassLookup** ppClassLookup);
@@ -645,6 +646,7 @@
     /* get start offset, and adjust deps start for 64-bit alignment */
     off_t depsOffset, auxOffset, endOffset, adjOffset;
     int depsLength, auxLength;
+    u4 optChecksum;
 
     depsOffset = lseek(fd, 0, SEEK_END);
     if (depsOffset < 0) {
@@ -690,6 +692,13 @@
     endOffset = lseek(fd, 0, SEEK_END);
     auxLength = endOffset - auxOffset;
 
+    /* compute checksum from start of deps to end of aux area */
+    if (!computeFileChecksum(fd, depsOffset,
+            (auxOffset+auxLength) - depsOffset, &optChecksum))
+    {
+        goto bail;
+    }
+
     /*
      * Output the "opt" header with all values filled in and a correct
      * magic number.
@@ -706,6 +715,7 @@
     optHdr.auxLength = (u4) auxLength;
 
     optHdr.flags = headerFlags;
+    optHdr.checksum = optChecksum;
 
     ssize_t actual;
     lseek(fd, 0, SEEK_SET);
@@ -1160,6 +1170,45 @@
         msg, (int)actual, (int)expected, strerror(err));
 }
 
+/*
+ * Compute a checksum on a piece of an open file.
+ *
+ * File will be positioned at end of checksummed area.
+ *
+ * Returns "true" on success.
+ */
+static bool computeFileChecksum(int fd, off_t start, size_t length, u4* pSum)
+{
+    unsigned char readBuf[8192];
+    ssize_t actual;
+    uLong adler;
+
+    if (lseek(fd, start, SEEK_SET) != start) {
+        LOGE("Unable to seek to start of checksum area (%ld): %s\n",
+            (long) start, strerror(errno));
+        return false;
+    }
+
+    adler = adler32(0L, Z_NULL, 0);
+
+    while (length != 0) {
+        size_t wanted = (length < sizeof(readBuf)) ? length : sizeof(readBuf);
+        actual = read(fd, readBuf, wanted);
+        if (actual <= 0) {
+            LOGE("Read failed (%d) while computing checksum (len=%zu): %s\n",
+                (int) actual, length, strerror(errno));
+            return false;
+        }
+
+        adler = adler32(adler, readBuf, actual);
+
+        length -= actual;
+    }
+
+    *pSum = adler;
+    return true;
+}
+
 
 /*
  * ===========================================================================