summaryrefslogtreecommitdiffstats
path: root/libdex/DexSwapVerify.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libdex/DexSwapVerify.cpp')
-rw-r--r--libdex/DexSwapVerify.cpp2946
1 files changed, 2946 insertions, 0 deletions
diff --git a/libdex/DexSwapVerify.cpp b/libdex/DexSwapVerify.cpp
new file mode 100644
index 000000000..13259370d
--- /dev/null
+++ b/libdex/DexSwapVerify.cpp
@@ -0,0 +1,2946 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Byte-swapping and verification of dex files.
+ */
+
+#include "DexFile.h"
+#include "DexClass.h"
+#include "DexDataMap.h"
+#include "DexProto.h"
+#include "DexUtf.h"
+#include "Leb128.h"
+
+#include <safe_iop.h>
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __BYTE_ORDER
+# error "byte ordering not defined"
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define SWAP2(_value) (_value)
+# define SWAP4(_value) (_value)
+# define SWAP8(_value) (_value)
+#else
+# define SWAP2(_value) endianSwapU2((_value))
+# define SWAP4(_value) endianSwapU4((_value))
+# define SWAP8(_value) endianSwapU8((_value))
+static u2 endianSwapU2(u2 value) {
+ return (value >> 8) | (value << 8);
+}
+static u4 endianSwapU4(u4 value) {
+ /* ABCD --> CDAB --> DCBA */
+ value = (value >> 16) | (value << 16);
+ return ((value & 0xff00ff00) >> 8) | ((value << 8) & 0xff00ff00);
+}
+static u8 endianSwapU8(u8 value) {
+ /* ABCDEFGH --> EFGHABCD --> GHEFCDAB --> HGFEDCBA */
+ value = (value >> 32) | (value << 32);
+ value = ((value & 0xffff0000ffff0000ULL) >> 16) |
+ ((value << 16) & 0xffff0000ffff0000ULL);
+ return ((value & 0xff00ff00ff00ff00ULL) >> 8) |
+ ((value << 8) & 0xff00ff00ff00ff00ULL);
+}
+#endif
+
+#define SWAP_FIELD2(_field) (_field) = SWAP2(_field)
+#define SWAP_FIELD4(_field) (_field) = SWAP4(_field)
+#define SWAP_FIELD8(_field) (_field) = SWAP8(_field)
+
+/*
+ * Some information we pass around to help verify values.
+ */
+typedef struct CheckState {
+ const DexHeader* pHeader;
+ const u1* fileStart;
+ const u1* fileEnd; // points to fileStart + fileLen
+ u4 fileLen;
+ DexDataMap* pDataMap; // set after map verification
+ const DexFile* pDexFile; // set after intraitem verification
+
+ /*
+ * bitmap of type_id indices that have been used to define classes;
+ * initialized immediately before class_def cross-verification, and
+ * freed immediately after it
+ */
+ u4* pDefinedClassBits;
+
+ const void* previousItem; // set during section iteration
+} CheckState;
+
+/*
+ * Return the file offset of the given pointer.
+ */
+static inline u4 fileOffset(const CheckState* state, const void* ptr) {
+ return ((const u1*) ptr) - state->fileStart;
+}
+
+/*
+ * Return a pointer for the given file offset.
+ */
+static inline void* filePointer(const CheckState* state, u4 offset) {
+ return (void*) (state->fileStart + offset);
+}
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ */
+static inline bool checkPtrRange(const CheckState* state,
+ const void* start, const void* end, const char* label) {
+ const void* fileStart = state->fileStart;
+ const void* fileEnd = state->fileEnd;
+ if ((start < fileStart) || (start > fileEnd)
+ || (end < start) || (end > fileEnd)) {
+ LOGW("Bad offset range for %s: 0x%x..0x%x\n", label,
+ fileOffset(state, start), fileOffset(state, end));
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Verify that a range of offsets, start inclusive to end exclusive,
+ * are all valid. That is, the start must indicate a valid byte or may
+ * point at the byte just past the end of the file (but no further),
+ * and the end must be no less than the start and must also not point
+ * beyond the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_OFFSET_RANGE(_start, _end) { \
+ const u1* _startPtr = (const u1*) filePointer(state, (_start)); \
+ const u1* _endPtr = (const u1*) filePointer(state, (_end)); \
+ if (!checkPtrRange(state, _startPtr, _endPtr, \
+ #_start ".." #_end)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_PTR_RANGE(_start, _end) { \
+ if (!checkPtrRange(state, (_start), (_end), #_start ".." #_end)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Make sure a list of items fits entirely within the file.
+ *
+ * Assumes "const CheckState* state" and "typeof(_count) == typeof(_elemSize)"
+ * If the type sizes or signs are mismatched, this will return 0.
+ */
+#define CHECK_LIST_SIZE(_ptr, _count, _elemSize) { \
+ const u1* _start = (const u1*) (_ptr); \
+ const u1* _end = _start + ((_count) * (_elemSize)); \
+ if (!safe_mul(NULL, (_count), (_elemSize)) || \
+ !checkPtrRange(state, _start, _end, #_ptr)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap a field that is known to hold an absolute DEX file offset. Note:
+ * This does not check to see that the swapped offset points within the
+ * mapped file, since that should be handled (with even more rigor) by
+ * the cross-verification phase.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define SWAP_OFFSET4(_field) { \
+ SWAP_FIELD4((_field)); \
+ }
+
+/*
+ * Verify that an index falls in a valid range.
+ */
+#define CHECK_INDEX(_field, _limit) { \
+ if ((_field) >= (_limit)) { \
+ LOGW("Bad index: %s(%u) > %s(%u)\n", \
+ #_field, (u4)(_field), #_limit, (u4)(_limit)); \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX2(_field, _limit) { \
+ SWAP_FIELD2((_field)); \
+ CHECK_INDEX((_field), (_limit)); \
+ }
+
+/*
+ * Verify that an index falls in a valid range or is kDexNoIndex.
+ */
+#define CHECK_INDEX_OR_NOINDEX(_field, _limit) { \
+ if ((_field) != kDexNoIndex && (_field) >= (_limit)) { \
+ LOGW("Bad index: %s(%u) > %s(%u)\n", \
+ #_field, (u4)(_field), #_limit, (u4)(_limit)); \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX4(_field, _limit) { \
+ SWAP_FIELD4((_field)); \
+ CHECK_INDEX((_field), (_limit)); \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range or is
+ * kDexNoIndex.
+ */
+#define SWAP_INDEX4_OR_NOINDEX(_field, _limit) { \
+ SWAP_FIELD4((_field)); \
+ CHECK_INDEX_OR_NOINDEX((_field), (_limit)); \
+ }
+
+/* Verify the definer of a given field_idx. */
+static bool verifyFieldDefiner(const CheckState* state, u4 definingClass,
+ u4 fieldIdx) {
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx == definingClass;
+}
+
+/* Verify the definer of a given method_idx. */
+static bool verifyMethodDefiner(const CheckState* state, u4 definingClass,
+ u4 methodIdx) {
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx == definingClass;
+}
+
+/*
+ * Calculate the required size (in elements) of the array pointed at by
+ * pDefinedClassBits.
+ */
+static size_t calcDefinedClassBitsSize(const CheckState* state)
+{
+ // Divide typeIdsSize by 32 (0x20), rounding up.
+ return (state->pHeader->typeIdsSize + 0x1f) >> 5;
+}
+
+/*
+ * Set the given bit in pDefinedClassBits, returning its former value.
+ */
+static bool setDefinedClassBit(const CheckState* state, u4 typeIdx) {
+ u4 arrayIdx = typeIdx >> 5;
+ u4 bit = 1 << (typeIdx & 0x1f);
+ u4* element = &state->pDefinedClassBits[arrayIdx];
+ bool result = (*element & bit) != 0;
+
+ *element |= bit;
+
+ return result;
+}
+
+/*
+ * Swap the header_item.
+ */
+static bool swapDexHeader(const CheckState* state, DexHeader* pHeader)
+{
+ CHECK_PTR_RANGE(pHeader, pHeader + 1);
+
+ // magic is ok
+ SWAP_FIELD4(pHeader->checksum);
+ // signature is ok
+ SWAP_FIELD4(pHeader->fileSize);
+ SWAP_FIELD4(pHeader->headerSize);
+ SWAP_FIELD4(pHeader->endianTag);
+ SWAP_FIELD4(pHeader->linkSize);
+ SWAP_OFFSET4(pHeader->linkOff);
+ SWAP_OFFSET4(pHeader->mapOff);
+ SWAP_FIELD4(pHeader->stringIdsSize);
+ SWAP_OFFSET4(pHeader->stringIdsOff);
+ SWAP_FIELD4(pHeader->typeIdsSize);
+ SWAP_OFFSET4(pHeader->typeIdsOff);
+ SWAP_FIELD4(pHeader->fieldIdsSize);
+ SWAP_OFFSET4(pHeader->fieldIdsOff);
+ SWAP_FIELD4(pHeader->methodIdsSize);
+ SWAP_OFFSET4(pHeader->methodIdsOff);
+ SWAP_FIELD4(pHeader->protoIdsSize);
+ SWAP_OFFSET4(pHeader->protoIdsOff);
+ SWAP_FIELD4(pHeader->classDefsSize);
+ SWAP_OFFSET4(pHeader->classDefsOff);
+ SWAP_FIELD4(pHeader->dataSize);
+ SWAP_OFFSET4(pHeader->dataOff);
+
+ if (pHeader->endianTag != kDexEndianConstant) {
+ LOGE("Unexpected endian_tag: 0x%x\n", pHeader->endianTag);
+ return false;
+ }
+
+ // Assign variables so the diagnostic is prettier. (Hooray for macros.)
+ u4 linkOff = pHeader->linkOff;
+ u4 linkEnd = linkOff + pHeader->linkSize;
+ u4 dataOff = pHeader->dataOff;
+ u4 dataEnd = dataOff + pHeader->dataSize;
+ CHECK_OFFSET_RANGE(linkOff, linkEnd);
+ CHECK_OFFSET_RANGE(dataOff, dataEnd);
+
+ /*
+ * Note: The offsets and ranges of the other header items end up getting
+ * checked during the first iteration over the map.
+ */
+
+ return true;
+}
+
+/* Check the header section for sanity. */
+static bool checkHeaderSection(const CheckState* state, u4 sectionOffset,
+ u4 sectionCount, u4* endOffset) {
+ if (sectionCount != 1) {
+ LOGE("Multiple header items\n");
+ return false;
+ }
+
+ if (sectionOffset != 0) {
+ LOGE("Header at 0x%x; not at start of file\n", sectionOffset);
+ return false;
+ }
+
+ const DexHeader* pHeader = (const DexHeader*) filePointer(state, 0);
+ *endOffset = pHeader->headerSize;
+ return true;
+}
+
+/*
+ * Helper for swapMap(), which turns a map type constant into a small
+ * one-bit-on integer, suitable for use in an int-sized bit set.
+ */
+static u4 mapTypeToBitMask(int mapType) {
+ switch (mapType) {
+ case kDexTypeHeaderItem: return 1 << 0;
+ case kDexTypeStringIdItem: return 1 << 1;
+ case kDexTypeTypeIdItem: return 1 << 2;
+ case kDexTypeProtoIdItem: return 1 << 3;
+ case kDexTypeFieldIdItem: return 1 << 4;
+ case kDexTypeMethodIdItem: return 1 << 5;
+ case kDexTypeClassDefItem: return 1 << 6;
+ case kDexTypeMapList: return 1 << 7;
+ case kDexTypeTypeList: return 1 << 8;
+ case kDexTypeAnnotationSetRefList: return 1 << 9;
+ case kDexTypeAnnotationSetItem: return 1 << 10;
+ case kDexTypeClassDataItem: return 1 << 11;
+ case kDexTypeCodeItem: return 1 << 12;
+ case kDexTypeStringDataItem: return 1 << 13;
+ case kDexTypeDebugInfoItem: return 1 << 14;
+ case kDexTypeAnnotationItem: return 1 << 15;
+ case kDexTypeEncodedArrayItem: return 1 << 16;
+ case kDexTypeAnnotationsDirectoryItem: return 1 << 17;
+ default: {
+ LOGE("Unknown map item type %04x\n", mapType);
+ return 0;
+ }
+ }
+}
+
+/*
+ * Helper for swapMap(), which indicates if an item type should appear
+ * in the data section.
+ */
+static bool isDataSectionType(int mapType) {
+ switch (mapType) {
+ case kDexTypeHeaderItem:
+ case kDexTypeStringIdItem:
+ case kDexTypeTypeIdItem:
+ case kDexTypeProtoIdItem:
+ case kDexTypeFieldIdItem:
+ case kDexTypeMethodIdItem:
+ case kDexTypeClassDefItem: {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Swap the map_list and verify what we can about it. Also, if verification
+ * passes, allocate the state's DexDataMap.
+ */
+static bool swapMap(CheckState* state, DexMapList* pMap)
+{
+ DexMapItem* item = pMap->list;
+ u4 count;
+ u4 dataItemCount = 0; // Total count of items in the data section.
+ u4 dataItemsLeft = state->pHeader->dataSize; // See use below.
+ u4 usedBits = 0; // Bit set: one bit per section
+ bool first = true;
+ u4 lastOffset = 0;
+
+ SWAP_FIELD4(pMap->size);
+ count = pMap->size;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexMapItem));
+
+ while (count--) {
+ SWAP_FIELD2(item->type);
+ SWAP_FIELD2(item->unused);
+ SWAP_FIELD4(item->size);
+ SWAP_OFFSET4(item->offset);
+
+ if (first) {
+ first = false;
+ } else if (lastOffset >= item->offset) {
+ LOGE("Out-of-order map item: 0x%x then 0x%x\n",
+ lastOffset, item->offset);
+ return false;
+ }
+
+ if (item->offset >= state->pHeader->fileSize) {
+ LOGE("Map item after end of file: %x, size 0x%x\n",
+ item->offset, state->pHeader->fileSize);
+ return false;
+ }
+
+ if (isDataSectionType(item->type)) {
+ u4 icount = item->size;
+
+ /*
+ * This sanity check on the data section items ensures that
+ * there are no more items than the number of bytes in
+ * the data section.
+ */
+ if (icount > dataItemsLeft) {
+ LOGE("Unrealistically many items in the data section: "
+ "at least %d\n", dataItemCount + icount);
+ return false;
+ }
+
+ dataItemsLeft -= icount;
+ dataItemCount += icount;
+ }
+
+ u4 bit = mapTypeToBitMask(item->type);
+
+ if (bit == 0) {
+ return false;
+ }
+
+ if ((usedBits & bit) != 0) {
+ LOGE("Duplicate map section of type 0x%x\n", item->type);
+ return false;
+ }
+
+ usedBits |= bit;
+ lastOffset = item->offset;
+ item++;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeHeaderItem)) == 0) {
+ LOGE("Map is missing header entry\n");
+ return false;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeMapList)) == 0) {
+ LOGE("Map is missing map_list entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeStringIdItem)) == 0)
+ && ((state->pHeader->stringIdsOff != 0)
+ || (state->pHeader->stringIdsSize != 0))) {
+ LOGE("Map is missing string_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem)) == 0)
+ && ((state->pHeader->typeIdsOff != 0)
+ || (state->pHeader->typeIdsSize != 0))) {
+ LOGE("Map is missing type_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem)) == 0)
+ && ((state->pHeader->protoIdsOff != 0)
+ || (state->pHeader->protoIdsSize != 0))) {
+ LOGE("Map is missing proto_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem)) == 0)
+ && ((state->pHeader->fieldIdsOff != 0)
+ || (state->pHeader->fieldIdsSize != 0))) {
+ LOGE("Map is missing field_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem)) == 0)
+ && ((state->pHeader->methodIdsOff != 0)
+ || (state->pHeader->methodIdsSize != 0))) {
+ LOGE("Map is missing method_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeClassDefItem)) == 0)
+ && ((state->pHeader->classDefsOff != 0)
+ || (state->pHeader->classDefsSize != 0))) {
+ LOGE("Map is missing class_defs entry\n");
+ return false;
+ }
+
+ state->pDataMap = dexDataMapAlloc(dataItemCount);
+ if (state->pDataMap == NULL) {
+ LOGE("Unable to allocate data map (size 0x%x)\n", dataItemCount);
+ return false;
+ }
+
+ return true;
+}
+
+/* Check the map section for sanity. */
+static bool checkMapSection(const CheckState* state, u4 sectionOffset,
+ u4 sectionCount, u4* endOffset) {
+ if (sectionCount != 1) {
+ LOGE("Multiple map list items");
+ return false;
+ }
+
+ if (sectionOffset != state->pHeader->mapOff) {
+ LOGE("Map not at header-defined offset: 0x%x, expected 0x%x\n",
+ sectionOffset, state->pHeader->mapOff);
+ return false;
+ }
+
+ const DexMapList* pMap = (const DexMapList*) filePointer(state, sectionOffset);
+
+ *endOffset =
+ sectionOffset + sizeof(u4) + (pMap->size * sizeof(DexMapItem));
+ return true;
+}
+
+/* Perform byte-swapping and intra-item verification on string_id_item. */
+static void* swapStringIdItem(const CheckState* state, void* ptr) {
+ DexStringId* item = (DexStringId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_OFFSET4(item->stringDataOff);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of string_id_item. */
+static void* crossVerifyStringIdItem(const CheckState* state, void* ptr) {
+ const DexStringId* item = (const DexStringId*) ptr;
+
+ if (!dexDataMapVerify(state->pDataMap,
+ item->stringDataOff, kDexTypeStringDataItem)) {
+ return NULL;
+ }
+
+ const DexStringId* item0 = (const DexStringId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering.
+ const char* s0 = dexGetStringData(state->pDexFile, item0);
+ const char* s1 = dexGetStringData(state->pDexFile, item);
+ if (dexUtf8Cmp(s0, s1) >= 0) {
+ LOGE("Out-of-order string_ids: '%s' then '%s'\n", s0, s1);
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on type_id_item. */
+static void* swapTypeIdItem(const CheckState* state, void* ptr) {
+ DexTypeId* item = (DexTypeId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->descriptorIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of type_id_item. */
+static void* crossVerifyTypeIdItem(const CheckState* state, void* ptr) {
+ const DexTypeId* item = (const DexTypeId*) ptr;
+ const char* descriptor =
+ dexStringById(state->pDexFile, item->descriptorIdx);
+
+ if (!dexIsValidTypeDescriptor(descriptor)) {
+ LOGE("Invalid type descriptor: '%s'\n", descriptor);
+ return NULL;
+ }
+
+ const DexTypeId* item0 = (const DexTypeId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on string_ids being in order.
+ if (item0->descriptorIdx >= item->descriptorIdx) {
+ LOGE("Out-of-order type_ids: 0x%x then 0x%x\n",
+ item0->descriptorIdx, item->descriptorIdx);
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on proto_id_item. */
+static void* swapProtoIdItem(const CheckState* state, void* ptr) {
+ DexProtoId* item = (DexProtoId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->shortyIdx, state->pHeader->stringIdsSize);
+ SWAP_INDEX4(item->returnTypeIdx, state->pHeader->typeIdsSize);
+ SWAP_OFFSET4(item->parametersOff);
+
+ return item + 1;
+}
+
+/* Helper for crossVerifyProtoIdItem(), which checks a shorty character
+ * to see if it is compatible with a type descriptor. Returns true if
+ * so, false if not. */
+static bool shortyDescMatch(char shorty, const char* descriptor, bool
+ isReturnType) {
+ switch (shorty) {
+ case 'V': {
+ if (!isReturnType) {
+ LOGE("Invalid use of void\n");
+ return false;
+ }
+ // Fall through.
+ }
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z': {
+ if ((descriptor[0] != shorty) || (descriptor[1] != '\0')) {
+ LOGE("Shorty vs. primitive type mismatch: '%c', '%s'\n",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ case 'L': {
+ if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+ LOGE("Shorty vs. type mismatch: '%c', '%s'\n",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ default: {
+ LOGE("Bogus shorty: '%c'\n", shorty);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Perform cross-item verification of proto_id_item. */
+static void* crossVerifyProtoIdItem(const CheckState* state, void* ptr) {
+ const DexProtoId* item = (const DexProtoId*) ptr;
+ const char* shorty =
+ dexStringById(state->pDexFile, item->shortyIdx);
+
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->parametersOff, kDexTypeTypeList)) {
+ return NULL;
+ }
+
+ if (!shortyDescMatch(*shorty,
+ dexStringByTypeIdx(state->pDexFile, item->returnTypeIdx),
+ true)) {
+ return NULL;
+ }
+
+ u4 protoIdx = item - state->pDexFile->pProtoIds;
+ DexProto proto = { state->pDexFile, protoIdx };
+ DexParameterIterator iterator;
+
+ dexParameterIteratorInit(&iterator, &proto);
+ shorty++; // Skip the return type.
+
+ for (;;) {
+ const char *desc = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (desc == NULL) {
+ break;
+ }
+
+ if (*shorty == '\0') {
+ LOGE("Shorty is too short\n");
+ return NULL;
+ }
+
+ if (!shortyDescMatch(*shorty, desc, false)) {
+ return NULL;
+ }
+
+ shorty++;
+ }
+
+ if (*shorty != '\0') {
+ LOGE("Shorty is too long\n");
+ return NULL;
+ }
+
+ const DexProtoId* item0 = (const DexProtoId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on type_ids being in order.
+ if (item0->returnTypeIdx > item->returnTypeIdx) {
+ LOGE("Out-of-order proto_id return types\n");
+ return NULL;
+ } else if (item0->returnTypeIdx == item->returnTypeIdx) {
+ bool badOrder = false;
+ DexProto proto0 = { state->pDexFile, protoIdx - 1 };
+ DexParameterIterator iterator0;
+
+ dexParameterIteratorInit(&iterator, &proto);
+ dexParameterIteratorInit(&iterator0, &proto0);
+
+ for (;;) {
+ u4 idx0 = dexParameterIteratorNextIndex(&iterator0);
+ u4 idx1 = dexParameterIteratorNextIndex(&iterator);
+
+ if (idx1 == kDexNoIndex) {
+ badOrder = true;
+ break;
+ }
+
+ if (idx0 == kDexNoIndex) {
+ break;
+ }
+
+ if (idx0 < idx1) {
+ break;
+ } else if (idx0 > idx1) {
+ badOrder = true;
+ break;
+ }
+ }
+
+ if (badOrder) {
+ LOGE("Out-of-order proto_id arguments\n");
+ return NULL;
+ }
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on field_id_item. */
+static void* swapFieldIdItem(const CheckState* state, void* ptr) {
+ DexFieldId* item = (DexFieldId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX2(item->typeIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of field_id_item. */
+static void* crossVerifyFieldIdItem(const CheckState* state, void* ptr) {
+ const DexFieldId* item = (const DexFieldId*) ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsClassDescriptor(s)) {
+ LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringByTypeIdx(state->pDexFile, item->typeIdx);
+ if (!dexIsFieldDescriptor(s)) {
+ LOGE("Invalid descriptor for type_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ LOGE("Invalid name: '%s'\n", s);
+ return NULL;
+ }
+
+ const DexFieldId* item0 = (const DexFieldId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on the other sections being in order.
+ bool done = false;
+ bool bogus = false;
+
+ if (item0->classIdx > item->classIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->classIdx < item->classIdx) {
+ done = true;
+ }
+
+ if (!done) {
+ if (item0->nameIdx > item->nameIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->nameIdx < item->nameIdx) {
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (item0->typeIdx >= item->typeIdx) {
+ bogus = true;
+ }
+ }
+
+ if (bogus) {
+ LOGE("Out-of-order field_ids\n");
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on method_id_item. */
+static void* swapMethodIdItem(const CheckState* state, void* ptr) {
+ DexMethodId* item = (DexMethodId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX2(item->protoIdx, state->pHeader->protoIdsSize);
+ SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of method_id_item. */
+static void* crossVerifyMethodIdItem(const CheckState* state, void* ptr) {
+ const DexMethodId* item = (const DexMethodId*) ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsReferenceDescriptor(s)) {
+ LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ LOGE("Invalid name: '%s'\n", s);
+ return NULL;
+ }
+
+ const DexMethodId* item0 = (const DexMethodId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on the other sections being in order.
+ bool done = false;
+ bool bogus = false;
+
+ if (item0->classIdx > item->classIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->classIdx < item->classIdx) {
+ done = true;
+ }
+
+ if (!done) {
+ if (item0->nameIdx > item->nameIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->nameIdx < item->nameIdx) {
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (item0->protoIdx >= item->protoIdx) {
+ bogus = true;
+ }
+ }
+
+ if (bogus) {
+ LOGE("Out-of-order method_ids\n");
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on class_def_item. */
+static void* swapClassDefItem(const CheckState* state, void* ptr) {
+ DexClassDef* item = (DexClassDef*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_FIELD4(item->accessFlags);
+ SWAP_INDEX4_OR_NOINDEX(item->superclassIdx, state->pHeader->typeIdsSize);
+ SWAP_OFFSET4(item->interfacesOff);
+ SWAP_INDEX4_OR_NOINDEX(item->sourceFileIdx, state->pHeader->stringIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+ SWAP_OFFSET4(item->classDataOff);
+
+ return item + 1;
+}
+
+/* defined below */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+ DexClassData* classData);
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+ const DexAnnotationsDirectoryItem* dir);
+
+/* Helper for crossVerifyClassDefItem(), which checks a class_data_item to
+ * make sure all its references are to a given class. */
+static bool verifyClassDataIsForDef(const CheckState* state, u4 offset,
+ u4 definerIdx) {
+ if (offset == 0) {
+ return true;
+ }
+
+ const u1* data = (const u1*) filePointer(state, offset);
+ DexClassData* classData = dexReadAndVerifyClassData(&data, NULL);
+
+ if (classData == NULL) {
+ // Shouldn't happen, but bail here just in case.
+ return false;
+ }
+
+ /*
+ * The class_data_item verification ensures that
+ * it consistently refers to the same definer, so all we need to
+ * do is check the first one.
+ */
+ u4 dataDefiner = findFirstClassDataDefiner(state, classData);
+ bool result = (dataDefiner == definerIdx) || (dataDefiner == kDexNoIndex);
+
+ free(classData);
+ return result;
+}
+
+/* Helper for crossVerifyClassDefItem(), which checks an
+ * annotations_directory_item to make sure all its references are to a
+ * given class. */
+static bool verifyAnnotationsDirectoryIsForDef(const CheckState* state,
+ u4 offset, u4 definerIdx) {
+ if (offset == 0) {
+ return true;
+ }
+
+ const DexAnnotationsDirectoryItem* dir =
+ (const DexAnnotationsDirectoryItem*) filePointer(state, offset);
+ u4 annoDefiner = findFirstAnnotationsDirectoryDefiner(state, dir);
+
+ return (annoDefiner == definerIdx) || (annoDefiner == kDexNoIndex);
+}
+
+/* Perform cross-item verification of class_def_item. */
+static void* crossVerifyClassDefItem(const CheckState* state, void* ptr) {
+ const DexClassDef* item = (const DexClassDef*) ptr;
+ u4 classIdx = item->classIdx;
+ const char* descriptor = dexStringByTypeIdx(state->pDexFile, classIdx);
+
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Invalid class: '%s'\n", descriptor);
+ return NULL;
+ }
+
+ if (setDefinedClassBit(state, classIdx)) {
+ LOGE("Duplicate class definition: '%s'\n", descriptor);
+ return NULL;
+ }
+
+ bool okay =
+ dexDataMapVerify0Ok(state->pDataMap,
+ item->interfacesOff, kDexTypeTypeList)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->annotationsOff, kDexTypeAnnotationsDirectoryItem)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->classDataOff, kDexTypeClassDataItem)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->staticValuesOff, kDexTypeEncodedArrayItem);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ if (item->superclassIdx != kDexNoIndex) {
+ descriptor = dexStringByTypeIdx(state->pDexFile, item->superclassIdx);
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Invalid superclass: '%s'\n", descriptor);
+ return NULL;
+ }
+ }
+
+ const DexTypeList* interfaces =
+ dexGetInterfacesList(state->pDexFile, item);
+ if (interfaces != NULL) {
+ u4 size = interfaces->size;
+ u4 i;
+
+ /*
+ * Ensure that all interfaces refer to classes (not arrays or
+ * primitives).
+ */
+ for (i = 0; i < size; i++) {
+ descriptor = dexStringByTypeIdx(state->pDexFile,
+ dexTypeListGetIdx(interfaces, i));
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Invalid interface: '%s'\n", descriptor);
+ return NULL;
+ }
+ }
+
+ /*
+ * Ensure that there are no duplicates. This is an O(N^2) test,
+ * but in practice the number of interfaces implemented by any
+ * given class is low. I will buy a milkshake for the
+ * first person to show me a realistic case for which this test
+ * would be unacceptably slow.
+ */
+ for (i = 1; i < size; i++) {
+ u4 idx1 = dexTypeListGetIdx(interfaces, i);
+ u4 j;
+ for (j = 0; j < i; j++) {
+ u4 idx2 = dexTypeListGetIdx(interfaces, j);
+ if (idx1 == idx2) {
+ LOGE("Duplicate interface: '%s'\n",
+ dexStringByTypeIdx(state->pDexFile, idx1));
+ return NULL;
+ }
+ }
+ }
+ }
+
+ if (!verifyClassDataIsForDef(state, item->classDataOff, item->classIdx)) {
+ LOGE("Invalid class_data_item\n");
+ return NULL;
+ }
+
+ if (!verifyAnnotationsDirectoryIsForDef(state, item->annotationsOff,
+ item->classIdx)) {
+ LOGE("Invalid annotations_directory_item\n");
+ return NULL;
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's field elements. */
+static u1* swapFieldAnnotations(const CheckState* state, u4 count, u1* addr) {
+ DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexFieldAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->fieldIdx, state->pHeader->fieldIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->fieldIdx) {
+ LOGE("Out-of-order field_idx: 0x%x then 0x%x\n", lastIdx,
+ item->fieldIdx);
+ return NULL;
+ }
+
+ lastIdx = item->fieldIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's method elements. */
+static u1* swapMethodAnnotations(const CheckState* state, u4 count, u1* addr) {
+ DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexMethodAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", lastIdx,
+ item->methodIdx);
+ return NULL;
+ }
+
+ lastIdx = item->methodIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's parameter elements. */
+static u1* swapParameterAnnotations(const CheckState* state, u4 count,
+ u1* addr) {
+ DexParameterAnnotationsItem* item = (DexParameterAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexParameterAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", lastIdx,
+ item->methodIdx);
+ return NULL;
+ }
+
+ lastIdx = item->methodIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotations_directory_item. */
+static void* swapAnnotationsDirectoryItem(const CheckState* state, void* ptr) {
+ DexAnnotationsDirectoryItem* item = (DexAnnotationsDirectoryItem*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_OFFSET4(item->classAnnotationsOff);
+ SWAP_FIELD4(item->fieldsSize);
+ SWAP_FIELD4(item->methodsSize);
+ SWAP_FIELD4(item->parametersSize);
+
+ u1* addr = (u1*) (item + 1);
+
+ if (item->fieldsSize != 0) {
+ addr = swapFieldAnnotations(state, item->fieldsSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->methodsSize != 0) {
+ addr = swapMethodAnnotations(state, item->methodsSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->parametersSize != 0) {
+ addr = swapParameterAnnotations(state, item->parametersSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ return addr;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * field elements. */
+static const u1* crossVerifyFieldAnnotations(const CheckState* state, u4 count,
+ const u1* addr, u4 definingClass) {
+ const DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyFieldDefiner(state, definingClass, item->fieldIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * method elements. */
+static const u1* crossVerifyMethodAnnotations(const CheckState* state,
+ u4 count, const u1* addr, u4 definingClass) {
+ const DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * parameter elements. */
+static const u1* crossVerifyParameterAnnotations(const CheckState* state,
+ u4 count, const u1* addr, u4 definingClass) {
+ const DexParameterAnnotationsItem* item =
+ (DexParameterAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetRefList)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyAnnotationsDirectoryItem(), which finds the type_idx of
+ * the definer of the first item in the data. */
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+ const DexAnnotationsDirectoryItem* dir) {
+ if (dir->fieldsSize != 0) {
+ const DexFieldAnnotationsItem* fields =
+ dexGetFieldAnnotations(state->pDexFile, dir);
+ const DexFieldId* field =
+ dexGetFieldId(state->pDexFile, fields[0].fieldIdx);
+ return field->classIdx;
+ }
+
+ if (dir->methodsSize != 0) {
+ const DexMethodAnnotationsItem* methods =
+ dexGetMethodAnnotations(state->pDexFile, dir);
+ const DexMethodId* method =
+ dexGetMethodId(state->pDexFile, methods[0].methodIdx);
+ return method->classIdx;
+ }
+
+ if (dir->parametersSize != 0) {
+ const DexParameterAnnotationsItem* parameters =
+ dexGetParameterAnnotations(state->pDexFile, dir);
+ const DexMethodId* method =
+ dexGetMethodId(state->pDexFile, parameters[0].methodIdx);
+ return method->classIdx;
+ }
+
+ return kDexNoIndex;
+}
+
+/* Perform cross-item verification of annotations_directory_item. */
+static void* crossVerifyAnnotationsDirectoryItem(const CheckState* state,
+ void* ptr) {
+ const DexAnnotationsDirectoryItem* item = (const DexAnnotationsDirectoryItem*) ptr;
+ u4 definingClass = findFirstAnnotationsDirectoryDefiner(state, item);
+
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->classAnnotationsOff, kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+
+ const u1* addr = (const u1*) (item + 1);
+
+ if (item->fieldsSize != 0) {
+ addr = crossVerifyFieldAnnotations(state, item->fieldsSize, addr,
+ definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->methodsSize != 0) {
+ addr = crossVerifyMethodAnnotations(state, item->methodsSize, addr,
+ definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->parametersSize != 0) {
+ addr = crossVerifyParameterAnnotations(state, item->parametersSize,
+ addr, definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ return (void*) addr;
+}
+
+/* Perform byte-swapping and intra-item verification on type_list. */
+static void* swapTypeList(const CheckState* state, void* ptr)
+{
+ DexTypeList* pTypeList = (DexTypeList*) ptr;
+ DexTypeItem* pType;
+ u4 count;
+
+ CHECK_PTR_RANGE(pTypeList, pTypeList + 1);
+ SWAP_FIELD4(pTypeList->size);
+ count = pTypeList->size;
+ pType = pTypeList->list;
+ CHECK_LIST_SIZE(pType, count, sizeof(DexTypeItem));
+
+ while (count--) {
+ SWAP_INDEX2(pType->typeIdx, state->pHeader->typeIdsSize);
+ pType++;
+ }
+
+ return pType;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_ref_list. */
+static void* swapAnnotationSetRefList(const CheckState* state, void* ptr) {
+ DexAnnotationSetRefList* list = (DexAnnotationSetRefList*) ptr;
+ DexAnnotationSetRefItem* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(list, list + 1);
+ SWAP_FIELD4(list->size);
+ count = list->size;
+ item = list->list;
+ CHECK_LIST_SIZE(item, count, sizeof(DexAnnotationSetRefItem));
+
+ while (count--) {
+ SWAP_OFFSET4(item->annotationsOff);
+ item++;
+ }
+
+ return item;
+}
+
+/* Perform cross-item verification of annotation_set_ref_list. */
+static void* crossVerifyAnnotationSetRefList(const CheckState* state,
+ void* ptr) {
+ const DexAnnotationSetRefList* list = (const DexAnnotationSetRefList*) ptr;
+ const DexAnnotationSetRefItem* item = list->list;
+ int count = list->size;
+
+ while (count--) {
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->annotationsOff, kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (void*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_item. */
+static void* swapAnnotationSetItem(const CheckState* state, void* ptr) {
+ DexAnnotationSetItem* set = (DexAnnotationSetItem*) ptr;
+ u4* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(set, set + 1);
+ SWAP_FIELD4(set->size);
+ count = set->size;
+ item = set->entries;
+ CHECK_LIST_SIZE(item, count, sizeof(u4));
+
+ while (count--) {
+ SWAP_OFFSET4(*item);
+ item++;
+ }
+
+ return item;
+}
+
+/* Helper for crossVerifyAnnotationSetItem(), which extracts the type_idx
+ * out of an annotation_item. */
+static u4 annotationItemTypeIdx(const DexAnnotationItem* item) {
+ const u1* data = item->annotation;
+ return readUnsignedLeb128(&data);
+}
+
+/* Perform cross-item verification of annotation_set_item. */
+static void* crossVerifyAnnotationSetItem(const CheckState* state, void* ptr) {
+ const DexAnnotationSetItem* set = (const DexAnnotationSetItem*) ptr;
+ int count = set->size;
+ u4 lastIdx = 0;
+ bool first = true;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ dexGetAnnotationOff(set, i), kDexTypeAnnotationItem)) {
+ return NULL;
+ }
+
+ const DexAnnotationItem* annotation =
+ dexGetAnnotationItem(state->pDexFile, set, i);
+ u4 idx = annotationItemTypeIdx(annotation);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= idx) {
+ LOGE("Out-of-order entry types: 0x%x then 0x%x\n",
+ lastIdx, idx);
+ return NULL;
+ }
+
+ lastIdx = idx;
+ }
+
+ return (void*) (set->entries + count);
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of fields. */
+static bool verifyFields(const CheckState* state, u4 size,
+ DexField* fields, bool expectStatic) {
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ DexField* field = &fields[i];
+ u4 accessFlags = field->accessFlags;
+ bool isStatic = (accessFlags & ACC_STATIC) != 0;
+
+ CHECK_INDEX(field->fieldIdx, state->pHeader->fieldIdsSize);
+
+ if (isStatic != expectStatic) {
+ LOGE("Field in wrong list @ %d\n", i);
+ return false;
+ }
+
+ if ((accessFlags & ~ACC_FIELD_MASK) != 0) {
+ LOGE("Bogus field access flags %x @ %d\n", accessFlags, i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of methods. */
+static bool verifyMethods(const CheckState* state, u4 size,
+ DexMethod* methods, bool expectDirect) {
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ DexMethod* method = &methods[i];
+
+ CHECK_INDEX(method->methodIdx, state->pHeader->methodIdsSize);
+
+ u4 accessFlags = method->accessFlags;
+ bool isDirect =
+ (accessFlags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) != 0;
+ bool expectCode = (accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0;
+ bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;
+ bool allowSynchronized = (accessFlags & ACC_NATIVE) != 0;
+
+ if (isDirect != expectDirect) {
+ LOGE("Method in wrong list @ %d\n", i);
+ return false;
+ }
+
+ if (((accessFlags & ~ACC_METHOD_MASK) != 0)
+ || (isSynchronized && !allowSynchronized)) {
+ LOGE("Bogus method access flags %x @ %d\n", accessFlags, i);
+ return false;
+ }
+
+ if (expectCode) {
+ if (method->codeOff == 0) {
+ LOGE("Unexpected zero code_off for access_flags %x\n",
+ accessFlags);
+ return false;
+ }
+ } else if (method->codeOff != 0) {
+ LOGE("Unexpected non-zero code_off 0x%x for access_flags %x\n",
+ method->codeOff, accessFlags);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Helper for verifyClassDataItem(), which does most of the work. */
+static bool verifyClassDataItem0(const CheckState* state,
+ DexClassData* classData) {
+ bool okay;
+
+ okay = verifyFields(state, classData->header.staticFieldsSize,
+ classData->staticFields, true);
+
+ if (!okay) {
+ LOGE("Trouble with static fields\n");
+ return false;
+ }
+
+ verifyFields(state, classData->header.instanceFieldsSize,
+ classData->instanceFields, false);
+
+ if (!okay) {
+ LOGE("Trouble with instance fields\n");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.directMethodsSize,
+ classData->directMethods, true);
+
+ if (!okay) {
+ LOGE("Trouble with direct methods\n");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.virtualMethodsSize,
+ classData->virtualMethods, false);
+
+ if (!okay) {
+ LOGE("Trouble with virtual methods\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* Perform intra-item verification on class_data_item. */
+static void* intraVerifyClassDataItem(const CheckState* state, void* ptr) {
+ const u1* data = (const u1*) ptr;
+ DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+
+ if (classData == NULL) {
+ LOGE("Unable to parse class_data_item\n");
+ return NULL;
+ }
+
+ bool okay = verifyClassDataItem0(state, classData);
+
+ free(classData);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyClassDataItem(), which finds the type_idx of the definer
+ * of the first item in the data. */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+ DexClassData* classData) {
+ if (classData->header.staticFieldsSize != 0) {
+ u4 fieldIdx = classData->staticFields[0].fieldIdx;
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx;
+ }
+
+ if (classData->header.instanceFieldsSize != 0) {
+ u4 fieldIdx = classData->instanceFields[0].fieldIdx;
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx;
+ }
+
+ if (classData->header.directMethodsSize != 0) {
+ u4 methodIdx = classData->directMethods[0].methodIdx;
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx;
+ }
+
+ if (classData->header.virtualMethodsSize != 0) {
+ u4 methodIdx = classData->virtualMethods[0].methodIdx;
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx;
+ }
+
+ return kDexNoIndex;
+}
+
+/* Perform cross-item verification of class_data_item. */
+static void* crossVerifyClassDataItem(const CheckState* state, void* ptr) {
+ const u1* data = (const u1*) ptr;
+ DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+ u4 definingClass = findFirstClassDataDefiner(state, classData);
+ bool okay = true;
+ u4 i;
+
+ for (i = classData->header.staticFieldsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexField* field = &classData->staticFields[i];
+ okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+ }
+
+ for (i = classData->header.instanceFieldsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexField* field = &classData->instanceFields[i];
+ okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+ }
+
+ for (i = classData->header.directMethodsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexMethod* meth = &classData->directMethods[i];
+ okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+ kDexTypeCodeItem)
+ && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+ }
+
+ for (i = classData->header.virtualMethodsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexMethod* meth = &classData->virtualMethods[i];
+ okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+ kDexTypeCodeItem)
+ && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+ }
+
+ free(classData);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Helper for swapCodeItem(), which fills an array with all the valid
+ * handlerOff values for catch handlers and also verifies the handler
+ * contents. */
+static u4 setHandlerOffsAndVerify(const CheckState* state,
+ DexCode* code, u4 firstOffset, u4 handlersSize, u4* handlerOffs) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* handlersBase = dexGetCatchHandlerData(code);
+ u4 offset = firstOffset;
+ bool okay = true;
+ u4 i;
+
+ for (i = 0; i < handlersSize; i++) {
+ const u1* ptr = handlersBase + offset;
+ int size = readAndVerifySignedLeb128(&ptr, fileEnd, &okay);
+ bool catchAll;
+
+ if (!okay) {
+ LOGE("Bogus size\n");
+ return 0;
+ }
+
+ if ((size < -65536) || (size > 65536)) {
+ LOGE("Invalid size: %d\n", size);
+ return 0;
+ }
+
+ if (size <= 0) {
+ catchAll = true;
+ size = -size;
+ } else {
+ catchAll = false;
+ }
+
+ handlerOffs[i] = offset;
+
+ while (size-- > 0) {
+ u4 typeIdx =
+ readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus type_idx");
+ return 0;
+ }
+
+ CHECK_INDEX(typeIdx, state->pHeader->typeIdsSize);
+
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ LOGE("Invalid addr: 0x%x", addr);
+ return 0;
+ }
+ }
+
+ if (catchAll) {
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus catch_all_addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ LOGE("Invalid catch_all_addr: 0x%x", addr);
+ return 0;
+ }
+ }
+
+ offset = ptr - handlersBase;
+ }
+
+ return offset;
+}
+
+/* Helper for swapCodeItem(), which does all the try-catch related
+ * swapping and verification. */
+static void* swapTriesAndCatches(const CheckState* state, DexCode* code) {
+ const u1* encodedHandlers = dexGetCatchHandlerData(code);
+ const u1* encodedPtr = encodedHandlers;
+ bool okay = true;
+ u4 handlersSize =
+ readAndVerifyUnsignedLeb128(&encodedPtr, state->fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus handlers_size\n");
+ return NULL;
+ }
+
+ if ((handlersSize == 0) || (handlersSize >= 65536)) {
+ LOGE("Invalid handlers_size: %d\n", handlersSize);
+ return NULL;
+ }
+
+ u4 handlerOffs[handlersSize]; // list of valid handlerOff values
+ u4 endOffset = setHandlerOffsAndVerify(state, code,
+ encodedPtr - encodedHandlers,
+ handlersSize, handlerOffs);
+
+ if (endOffset == 0) {
+ return NULL;
+ }
+
+ DexTry* tries = (DexTry*) dexGetTries(code);
+ u4 count = code->triesSize;
+ u4 lastEnd = 0;
+
+ CHECK_LIST_SIZE(tries, count, sizeof(DexTry));
+
+ while (count--) {
+ u4 i;
+
+ SWAP_FIELD4(tries->startAddr);
+ SWAP_FIELD2(tries->insnCount);
+ SWAP_FIELD2(tries->handlerOff);
+
+ if (tries->startAddr < lastEnd) {
+ LOGE("Out-of-order try\n");
+ return NULL;
+ }
+
+ if (tries->startAddr >= code->insnsSize) {
+ LOGE("Invalid start_addr: 0x%x\n", tries->startAddr);
+ return NULL;
+ }
+
+ for (i = 0; i < handlersSize; i++) {
+ if (tries->handlerOff == handlerOffs[i]) {
+ break;
+ }
+ }
+
+ if (i == handlersSize) {
+ LOGE("Bogus handler offset: 0x%x\n", tries->handlerOff);
+ return NULL;
+ }
+
+ lastEnd = tries->startAddr + tries->insnCount;
+
+ if (lastEnd > code->insnsSize) {
+ LOGE("Invalid insn_count: 0x%x (end addr 0x%x)\n",
+ tries->insnCount, lastEnd);
+ return NULL;
+ }
+
+ tries++;
+ }
+
+ return (u1*) encodedHandlers + endOffset;
+}
+
+/* Perform byte-swapping and intra-item verification on code_item. */
+static void* swapCodeItem(const CheckState* state, void* ptr) {
+ DexCode* item = (DexCode*) ptr;
+ u2* insns;
+ u4 count;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_FIELD2(item->registersSize);
+ SWAP_FIELD2(item->insSize);
+ SWAP_FIELD2(item->outsSize);
+ SWAP_FIELD2(item->triesSize);
+ SWAP_OFFSET4(item->debugInfoOff);
+ SWAP_FIELD4(item->insnsSize);
+
+ if (item->insSize > item->registersSize) {
+ LOGE("insSize (%u) > registersSize (%u)\n", item->insSize,
+ item->registersSize);
+ return NULL;
+ }
+
+ if ((item->outsSize > 5) && (item->outsSize > item->registersSize)) {
+ /*
+ * It's okay for outsSize to be up to five, even if registersSize
+ * is smaller, since the short forms of method invocation allow
+ * repetition of a register multiple times within a single parameter
+ * list. Longer parameter lists, though, need to be represented
+ * in-order in the register file.
+ */
+ LOGE("outsSize (%u) > registersSize (%u)\n", item->outsSize,
+ item->registersSize);
+ return NULL;
+ }
+
+ count = item->insnsSize;
+ insns = item->insns;
+ CHECK_LIST_SIZE(insns, count, sizeof(u2));
+
+ while (count--) {
+ *insns = SWAP2(*insns);
+ insns++;
+ }
+
+ if (item->triesSize == 0) {
+ ptr = insns;
+ } else {
+ if ((((u4) insns) & 3) != 0) {
+ // Four-byte alignment for the tries. Verify the spacer is a 0.
+ if (*insns != 0) {
+ LOGE("Non-zero padding: 0x%x\n", (u4) *insns);
+ return NULL;
+ }
+ }
+
+ ptr = swapTriesAndCatches(state, item);
+ }
+
+ return ptr;
+}
+
+/* Perform intra-item verification on string_data_item. */
+static void* intraVerifyStringDataItem(const CheckState* state, void* ptr) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* data = (const u1*) ptr;
+ bool okay = true;
+ u4 utf16Size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 i;
+
+ if (!okay) {
+ LOGE("Bogus utf16_size\n");
+ return NULL;
+ }
+
+ for (i = 0; i < utf16Size; i++) {
+ if (data >= fileEnd) {
+ LOGE("String data would go beyond end-of-file\n");
+ return NULL;
+ }
+
+ u1 byte1 = *(data++);
+
+ // Switch on the high four bits.
+ switch (byte1 >> 4) {
+ case 0x00: {
+ // Special case of bit pattern 0xxx.
+ if (byte1 == 0) {
+ LOGE("String shorter than indicated utf16_size 0x%x\n",
+ utf16Size);
+ return NULL;
+ }
+ break;
+ }
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07: {
+ // Bit pattern 0xxx. No need for any extra bytes or checks.
+ break;
+ }
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f: {
+ /*
+ * Bit pattern 10xx or 1111, which are illegal start bytes.
+ * Note: 1111 is valid for normal UTF-8, but not the
+ * modified UTF-8 used here.
+ */
+ LOGE("Illegal start byte 0x%x\n", byte1);
+ return NULL;
+ }
+ case 0x0e: {
+ // Bit pattern 1110, so there are two additional bytes.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte2);
+ return NULL;
+ }
+ u1 byte3 = *(data++);
+ if ((byte3 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte3);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6)
+ | (byte3 & 0x3f);
+ if (value < 0x800) {
+ LOGE("Illegal representation for value %x\n", value);
+ return NULL;
+ }
+ break;
+ }
+ case 0x0c:
+ case 0x0d: {
+ // Bit pattern 110x, so there is one additional byte.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte2);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
+ if ((value != 0) && (value < 0x80)) {
+ LOGE("Illegal representation for value %x\n", value);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ if (*(data++) != '\0') {
+ LOGE("String longer than indicated utf16_size 0x%x\n", utf16Size);
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Perform intra-item verification on debug_info_item. */
+static void* intraVerifyDebugInfoItem(const CheckState* state, void* ptr) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* data = (const u1*) ptr;
+ bool okay = true;
+ u4 i;
+
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus line_start\n");
+ return NULL;
+ }
+
+ u4 parametersSize =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus parameters_size\n");
+ return NULL;
+ }
+
+ if (parametersSize > 65536) {
+ LOGE("Invalid parameters_size: 0x%x\n", parametersSize);
+ return NULL;
+ }
+
+ for (i = 0; i < parametersSize; i++) {
+ u4 parameterName =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus parameter_name\n");
+ return NULL;
+ }
+
+ if (parameterName != 0) {
+ parameterName--;
+ CHECK_INDEX(parameterName, state->pHeader->stringIdsSize);
+ }
+ }
+
+ bool done = false;
+ while (!done) {
+ u1 opcode = *(data++);
+
+ switch (opcode) {
+ case DBG_END_SEQUENCE: {
+ done = true;
+ break;
+ }
+ case DBG_ADVANCE_PC: {
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ break;
+ }
+ case DBG_ADVANCE_LINE: {
+ readAndVerifySignedLeb128(&data, fileEnd, &okay);
+ break;
+ }
+ case DBG_START_LOCAL: {
+ u4 idx;
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ case DBG_END_LOCAL:
+ case DBG_RESTART_LOCAL: {
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ break;
+ }
+ case DBG_START_LOCAL_EXTENDED: {
+ u4 idx;
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ case DBG_SET_FILE: {
+ u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ default: {
+ // No arguments to parse for anything else.
+ }
+ }
+
+ if (!okay) {
+ LOGE("Bogus syntax for opcode %02x\n", opcode);
+ return NULL;
+ }
+ }
+
+ return (void*) data;
+}
+
+/* defined below */
+static const u1* verifyEncodedValue(const CheckState* state, const u1* data,
+ bool crossVerify);
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+ const u1* data, bool crossVerify);
+
+/* Helper for verifyEncodedValue(), which reads a 1- to 4- byte unsigned
+ * little endian value. */
+static u4 readUnsignedLittleEndian(const CheckState* state, const u1** pData,
+ u4 size) {
+ const u1* data = *pData;
+ u4 result = 0;
+ u4 i;
+
+ CHECK_PTR_RANGE(data, data + size);
+
+ for (i = 0; i < size; i++) {
+ result |= ((u4) *(data++)) << (i * 8);
+ }
+
+ *pData = data;
+ return result;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_array. */
+static const u1* verifyEncodedArray(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ bool okay = true;
+ u4 size = readAndVerifyUnsignedLeb128(&data, state->fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus encoded_array size\n");
+ return NULL;
+ }
+
+ while (size--) {
+ data = verifyEncodedValue(state, data, crossVerify);
+ if (data == NULL) {
+ LOGE("Bogus encoded_array value\n");
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_value. */
+static const u1* verifyEncodedValue(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ CHECK_PTR_RANGE(data, data + 1);
+
+ u1 headerByte = *(data++);
+ u4 valueType = headerByte & kDexAnnotationValueTypeMask;
+ u4 valueArg = headerByte >> kDexAnnotationValueArgShift;
+
+ switch (valueType) {
+ case kDexAnnotationByte: {
+ if (valueArg != 0) {
+ LOGE("Bogus byte size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data++;
+ break;
+ }
+ case kDexAnnotationShort:
+ case kDexAnnotationChar: {
+ if (valueArg > 1) {
+ LOGE("Bogus char/short size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationInt:
+ case kDexAnnotationFloat: {
+ if (valueArg > 3) {
+ LOGE("Bogus int/float size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationLong:
+ case kDexAnnotationDouble: {
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationString: {
+ if (valueArg > 3) {
+ LOGE("Bogus string size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ break;
+ }
+ case kDexAnnotationType: {
+ if (valueArg > 3) {
+ LOGE("Bogus type size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+ break;
+ }
+ case kDexAnnotationField:
+ case kDexAnnotationEnum: {
+ if (valueArg > 3) {
+ LOGE("Bogus field/enum size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->fieldIdsSize);
+ break;
+ }
+ case kDexAnnotationMethod: {
+ if (valueArg > 3) {
+ LOGE("Bogus method size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->methodIdsSize);
+ break;
+ }
+ case kDexAnnotationArray: {
+ if (valueArg != 0) {
+ LOGE("Bogus array value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedArray(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationAnnotation: {
+ if (valueArg != 0) {
+ LOGE("Bogus annotation value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedAnnotation(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationNull: {
+ if (valueArg != 0) {
+ LOGE("Bogus null value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ case kDexAnnotationBoolean: {
+ if (valueArg > 1) {
+ LOGE("Bogus boolean value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ default: {
+ LOGE("Bogus value_type 0x%x\n", valueType);
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_annotation. */
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ const u1* fileEnd = state->fileEnd;
+ bool okay = true;
+ u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation type_idx\n");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+
+ if (crossVerify) {
+ const char* descriptor = dexStringByTypeIdx(state->pDexFile, idx);
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Bogus annotation type: '%s'\n", descriptor);
+ return NULL;
+ }
+ }
+
+ u4 size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 lastIdx = 0;
+ bool first = true;
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation size\n");
+ return NULL;
+ }
+
+ while (size--) {
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation name_idx\n");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+
+ if (crossVerify) {
+ const char* name = dexStringById(state->pDexFile, idx);
+ if (!dexIsValidMemberName(name)) {
+ LOGE("Bogus annotation member name: '%s'\n", name);
+ return NULL;
+ }
+ }
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= idx) {
+ LOGE("Out-of-order encoded_annotation name_idx: 0x%x then 0x%x\n",
+ lastIdx, idx);
+ return NULL;
+ }
+
+ data = verifyEncodedValue(state, data, crossVerify);
+ lastIdx = idx;
+
+ if (data == NULL) {
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Perform intra-item verification on encoded_array_item. */
+static void* intraVerifyEncodedArrayItem(const CheckState* state, void* ptr) {
+ return (void*) verifyEncodedArray(state, (const u1*) ptr, false);
+}
+
+/* Perform intra-item verification on annotation_item. */
+static void* intraVerifyAnnotationItem(const CheckState* state, void* ptr) {
+ const u1* data = (const u1*) ptr;
+
+ CHECK_PTR_RANGE(data, data + 1);
+
+ switch (*(data++)) {
+ case kDexVisibilityBuild:
+ case kDexVisibilityRuntime:
+ case kDexVisibilitySystem: {
+ break;
+ }
+ default: {
+ LOGE("Bogus annotation visibility: 0x%x\n", *data);
+ return NULL;
+ }
+ }
+
+ return (void*) verifyEncodedAnnotation(state, data, false);
+}
+
+/* Perform cross-item verification on annotation_item. */
+static void* crossVerifyAnnotationItem(const CheckState* state, void* ptr) {
+ const u1* data = (const u1*) ptr;
+
+ // Skip the visibility byte.
+ data++;
+
+ return (void*) verifyEncodedAnnotation(state, data, true);
+}
+
+
+
+
+/*
+ * Function to visit an individual top-level item type.
+ */
+typedef void* ItemVisitorFunction(const CheckState* state, void* ptr);
+
+/*
+ * Iterate over all the items in a section, optionally updating the
+ * data map (done if mapType is passed as non-negative). The section
+ * must consist of concatenated items of the same type.
+ */
+static bool iterateSectionWithOptionalUpdate(CheckState* state,
+ u4 offset, u4 count, ItemVisitorFunction* func, u4 alignment,
+ u4* nextOffset, int mapType) {
+ u4 alignmentMask = alignment - 1;
+ u4 i;
+
+ state->previousItem = NULL;
+
+ for (i = 0; i < count; i++) {
+ u4 newOffset = (offset + alignmentMask) & ~alignmentMask;
+ u1* ptr = (u1*) filePointer(state, newOffset);
+
+ if (offset < newOffset) {
+ ptr = (u1*) filePointer(state, offset);
+ if (offset < newOffset) {
+ CHECK_OFFSET_RANGE(offset, newOffset);
+ while (offset < newOffset) {
+ if (*ptr != '\0') {
+ LOGE("Non-zero padding 0x%02x @ %x\n", *ptr, offset);
+ return false;
+ }
+ ptr++;
+ offset++;
+ }
+ }
+ }
+
+ u1* newPtr = (u1*) func(state, ptr);
+ newOffset = fileOffset(state, newPtr);
+
+ if (newPtr == NULL) {
+ LOGE("Trouble with item %d @ offset 0x%x\n", i, offset);
+ return false;
+ }
+
+ if (newOffset > state->fileLen) {
+ LOGE("Item %d @ offset 0x%x ends out of bounds\n", i, offset);
+ return false;
+ }
+
+ if (mapType >= 0) {
+ dexDataMapAdd(state->pDataMap, offset, mapType);
+ }
+
+ state->previousItem = ptr;
+ offset = newOffset;
+ }
+
+ if (nextOffset != NULL) {
+ *nextOffset = offset;
+ }
+
+ return true;
+}
+
+/*
+ * Iterate over all the items in a section. The section must consist of
+ * concatenated items of the same type. This variant will not update the data
+ * map.
+ */
+static bool iterateSection(CheckState* state, u4 offset, u4 count,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+ return iterateSectionWithOptionalUpdate(state, offset, count, func,
+ alignment, nextOffset, -1);
+}
+
+/*
+ * Like iterateSection(), but also check that the offset and count match
+ * a given pair of expected values.
+ */
+static bool checkBoundsAndIterateSection(CheckState* state,
+ u4 offset, u4 count, u4 expectedOffset, u4 expectedCount,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+ if (offset != expectedOffset) {
+ LOGE("Bogus offset for section: got 0x%x; expected 0x%x\n",
+ offset, expectedOffset);
+ return false;
+ }
+
+ if (count != expectedCount) {
+ LOGE("Bogus size for section: got 0x%x; expected 0x%x\n",
+ count, expectedCount);
+ return false;
+ }
+
+ return iterateSection(state, offset, count, func, alignment, nextOffset);
+}
+
+/*
+ * Like iterateSection(), but also update the data section map and
+ * check that all the items fall within the data section.
+ */
+static bool iterateDataSection(CheckState* state, u4 offset, u4 count,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset, int mapType) {
+ u4 dataStart = state->pHeader->dataOff;
+ u4 dataEnd = dataStart + state->pHeader->dataSize;
+
+ assert(nextOffset != NULL);
+
+ if ((offset < dataStart) || (offset >= dataEnd)) {
+ LOGE("Bogus offset for data subsection: 0x%x\n", offset);
+ return false;
+ }
+
+ if (!iterateSectionWithOptionalUpdate(state, offset, count, func,
+ alignment, nextOffset, mapType)) {
+ return false;
+ }
+
+ if (*nextOffset > dataEnd) {
+ LOGE("Out-of-bounds end of data subsection: 0x%x\n", *nextOffset);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Byte-swap all items in the given map except the header and the map
+ * itself, both of which should have already gotten swapped. This also
+ * does all possible intra-item verification, that is, verification
+ * that doesn't need to assume the sanctity of the contents of *other*
+ * items. The intra-item limitation is because at the time an item is
+ * asked to verify itself, it can't assume that the items it refers to
+ * have been byte-swapped and verified.
+ */
+static bool swapEverythingButHeaderAndMap(CheckState* state,
+ DexMapList* pMap) {
+ const DexMapItem* item = pMap->list;
+ u4 lastOffset = 0;
+ u4 count = pMap->size;
+ bool okay = true;
+
+ while (okay && count--) {
+ u4 sectionOffset = item->offset;
+ u4 sectionCount = item->size;
+ u2 type = item->type;
+
+ if (lastOffset < sectionOffset) {
+ CHECK_OFFSET_RANGE(lastOffset, sectionOffset);
+ const u1* ptr = (const u1*) filePointer(state, lastOffset);
+ while (lastOffset < sectionOffset) {
+ if (*ptr != '\0') {
+ LOGE("Non-zero padding 0x%02x before section start @ %x\n",
+ *ptr, lastOffset);
+ okay = false;
+ break;
+ }
+ ptr++;
+ lastOffset++;
+ }
+ } else if (lastOffset > sectionOffset) {
+ LOGE("Section overlap or out-of-order map: %x, %x\n",
+ lastOffset, sectionOffset);
+ okay = false;
+ }
+
+ if (!okay) {
+ break;
+ }
+
+ switch (type) {
+ case kDexTypeHeaderItem: {
+ /*
+ * The header got swapped very early on, but do some
+ * additional sanity checking here.
+ */
+ okay = checkHeaderSection(state, sectionOffset, sectionCount,
+ &lastOffset);
+ break;
+ }
+ case kDexTypeStringIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->stringIdsOff,
+ state->pHeader->stringIdsSize, swapStringIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeTypeIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->typeIdsOff,
+ state->pHeader->typeIdsSize, swapTypeIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeProtoIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->protoIdsOff,
+ state->pHeader->protoIdsSize, swapProtoIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeFieldIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->fieldIdsOff,
+ state->pHeader->fieldIdsSize, swapFieldIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeMethodIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->methodIdsOff,
+ state->pHeader->methodIdsSize, swapMethodIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeClassDefItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->classDefsOff,
+ state->pHeader->classDefsSize, swapClassDefItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeMapList: {
+ /*
+ * The map section was swapped early on, but do some
+ * additional sanity checking here.
+ */
+ okay = checkMapSection(state, sectionOffset, sectionCount,
+ &lastOffset);
+ break;
+ }
+ case kDexTypeTypeList: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapTypeList, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeAnnotationSetRefList: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationSetRefList, sizeof(u4), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationSetItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationSetItem, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeClassDataItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyClassDataItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeCodeItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapCodeItem, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeStringDataItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyStringDataItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeDebugInfoItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyDebugInfoItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyAnnotationItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeEncodedArrayItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyEncodedArrayItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationsDirectoryItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationsDirectoryItem, sizeof(u4), &lastOffset,
+ type);
+ break;
+ }
+ default: {
+ LOGE("Unknown map item type %04x\n", type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("Swap of section type %04x failed\n", type);
+ }
+
+ item++;
+ }
+
+ return okay;
+}
+
+/*
+ * Perform cross-item verification on everything that needs it. This
+ * pass is only called after all items are byte-swapped and
+ * intra-verified (checked for internal consistency).
+ */
+static bool crossVerifyEverything(CheckState* state, DexMapList* pMap)
+{
+ const DexMapItem* item = pMap->list;
+ u4 count = pMap->size;
+ bool okay = true;
+
+ while (okay && count--) {
+ u4 sectionOffset = item->offset;
+ u4 sectionCount = item->size;
+
+ switch (item->type) {
+ case kDexTypeHeaderItem:
+ case kDexTypeMapList:
+ case kDexTypeTypeList:
+ case kDexTypeCodeItem:
+ case kDexTypeStringDataItem:
+ case kDexTypeDebugInfoItem:
+ case kDexTypeAnnotationItem:
+ case kDexTypeEncodedArrayItem: {
+ // There is no need for cross-item verification for these.
+ break;
+ }
+ case kDexTypeStringIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyStringIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeTypeIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyTypeIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeProtoIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyProtoIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeFieldIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyFieldIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeMethodIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyMethodIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeClassDefItem: {
+ // Allocate (on the stack) the "observed class_def" bits.
+ size_t arraySize = calcDefinedClassBitsSize(state);
+ u4 definedClassBits[arraySize];
+ memset(definedClassBits, 0, arraySize * sizeof(u4));
+ state->pDefinedClassBits = definedClassBits;
+
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyClassDefItem, sizeof(u4), NULL);
+
+ state->pDefinedClassBits = NULL;
+ break;
+ }
+ case kDexTypeAnnotationSetRefList: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationSetRefList, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeAnnotationSetItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationSetItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeClassDataItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyClassDataItem, sizeof(u1), NULL);
+ break;
+ }
+ case kDexTypeAnnotationsDirectoryItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationsDirectoryItem, sizeof(u4), NULL);
+ break;
+ }
+ default: {
+ LOGE("Unknown map item type %04x\n", item->type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("Cross-item verify of section type %04x failed\n",
+ item->type);
+ }
+
+ item++;
+ }
+
+ return okay;
+}
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do
+ * structural verification. This is only required for code that opens
+ * "raw" DEX files, such as the DEX optimizer.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerify(u1* addr, int len)
+{
+ DexHeader* pHeader;
+ CheckState state;
+ bool okay = true;
+
+ memset(&state, 0, sizeof(state));
+ LOGV("+++ swapping and verifying\n");
+
+ /*
+ * Start by verifying the magic number. The caller verified that "len"
+ * says we have at least a header's worth of data.
+ */
+ pHeader = (DexHeader*) addr;
+ if (memcmp(pHeader->magic, DEX_MAGIC, 4) != 0) {
+ /* really shouldn't be here -- this is weird */
+ LOGE("ERROR: Can't byte swap: bad magic number "
+ "(0x%02x %02x %02x %02x)\n",
+ pHeader->magic[0], pHeader->magic[1],
+ pHeader->magic[2], pHeader->magic[3]);
+ okay = false;
+ }
+
+ if (okay && memcmp(pHeader->magic+4, DEX_MAGIC_VERS, 4) != 0) {
+ /* older or newer version we don't know how to read */
+ LOGE("ERROR: Can't byte swap: bad dex version "
+ "(0x%02x %02x %02x %02x)\n",
+ pHeader->magic[4], pHeader->magic[5],
+ pHeader->magic[6], pHeader->magic[7]);
+ okay = false;
+ }
+
+ if (okay) {
+ int expectedLen = (int) SWAP4(pHeader->fileSize);
+ if (len < expectedLen) {
+ LOGE("ERROR: Bad length: expected %d, got %d\n", expectedLen, len);
+ okay = false;
+ } else if (len != expectedLen) {
+ LOGW("WARNING: Odd length: expected %d, got %d\n", expectedLen,
+ len);
+ // keep going
+ }
+ }
+
+ if (okay) {
+ /*
+ * Compute the adler32 checksum and compare it to what's stored in
+ * the file. This isn't free, but chances are good that we just
+ * unpacked this from a jar file and have all of the pages sitting
+ * in memory, so it's pretty quick.
+ *
+ * This might be a big-endian system, so we need to do this before
+ * we byte-swap the header.
+ */
+ uLong adler = adler32(0L, Z_NULL, 0);
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+ u4 storedFileSize = SWAP4(pHeader->fileSize);
+ u4 expectedChecksum = SWAP4(pHeader->checksum);
+
+ adler = adler32(adler, ((const u1*) pHeader) + nonSum,
+ storedFileSize - nonSum);
+
+ if (adler != expectedChecksum) {
+ LOGE("ERROR: bad checksum (%08lx, expected %08x)\n",
+ adler, expectedChecksum);
+ okay = false;
+ }
+ }
+
+ if (okay) {
+ state.fileStart = addr;
+ state.fileEnd = addr + len;
+ state.fileLen = len;
+ state.pDexFile = NULL;
+ state.pDataMap = NULL;
+ state.pDefinedClassBits = NULL;
+ state.previousItem = NULL;
+
+ /*
+ * Swap the header and check the contents.
+ */
+ okay = swapDexHeader(&state, pHeader);
+ }
+
+ if (okay) {
+ state.pHeader = pHeader;
+
+ if (pHeader->headerSize < sizeof(DexHeader)) {
+ LOGE("ERROR: Small header size %d, struct %d\n",
+ pHeader->headerSize, (int) sizeof(DexHeader));
+ okay = false;
+ } else if (pHeader->headerSize > sizeof(DexHeader)) {
+ LOGW("WARNING: Large header size %d, struct %d\n",
+ pHeader->headerSize, (int) sizeof(DexHeader));
+ // keep going?
+ }
+ }
+
+ if (okay) {
+ /*
+ * Look for the map. Swap it and then use it to find and swap
+ * everything else.
+ */
+ if (pHeader->mapOff != 0) {
+ DexFile dexFile;
+ DexMapList* pDexMap = (DexMapList*) (addr + pHeader->mapOff);
+
+ okay = okay && swapMap(&state, pDexMap);
+ okay = okay && swapEverythingButHeaderAndMap(&state, pDexMap);
+
+ dexFileSetupBasicPointers(&dexFile, addr);
+ state.pDexFile = &dexFile;
+
+ okay = okay && crossVerifyEverything(&state, pDexMap);
+ } else {
+ LOGE("ERROR: No map found; impossible to byte-swap and verify");
+ okay = false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("ERROR: Byte swap + verify failed\n");
+ }
+
+ if (state.pDataMap != NULL) {
+ dexDataMapFree(state.pDataMap);
+ }
+
+ return !okay; // 0 == success
+}
+
+/*
+ * Detect the file type of the given memory buffer via magic number.
+ * Call dexSwapAndVerify() on an unoptimized DEX file, do nothing
+ * but return successfully on an optimized DEX file, and report an
+ * error for all other cases.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerifyIfNecessary(u1* addr, int len)
+{
+ if (memcmp(addr, DEX_OPT_MAGIC, 4) == 0) {
+ // It is an optimized dex file.
+ return 0;
+ }
+
+ if (memcmp(addr, DEX_MAGIC, 4) == 0) {
+ // It is an unoptimized dex file.
+ return dexSwapAndVerify(addr, len);
+ }
+
+ LOGE("ERROR: Bad magic number (0x%02x %02x %02x %02x)\n",
+ addr[0], addr[1], addr[2], addr[3]);
+
+ return 1;
+}