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;
+}
+
/*
* ===========================================================================