status_t ResTable::parsePackage()

in libresource/ResourceTypes.cpp [5855:6147]


status_t ResTable::parsePackage(const ResTable_package* const pkg,
                                const Header* const header)
{
    const uint8_t* base = (const uint8_t*)pkg;
    status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
                                  header->dataEnd, "ResTable_package");
    if (err != NO_ERROR) {
        return (mError=err);
    }

    const uint32_t pkgSize = dtohl(pkg->header.size);

    if (dtohl(pkg->typeStrings) >= pkgSize) {
        ALOGW("ResTable_package type strings at 0x%x are past chunk size 0x%x.",
             dtohl(pkg->typeStrings), pkgSize);
        return (mError=BAD_TYPE);
    }
    if ((dtohl(pkg->typeStrings)&0x3) != 0) {
        ALOGW("ResTable_package type strings at 0x%x is not on an integer boundary.",
             dtohl(pkg->typeStrings));
        return (mError=BAD_TYPE);
    }
    if (dtohl(pkg->keyStrings) >= pkgSize) {
        ALOGW("ResTable_package key strings at 0x%x are past chunk size 0x%x.",
             dtohl(pkg->keyStrings), pkgSize);
        return (mError=BAD_TYPE);
    }
    if ((dtohl(pkg->keyStrings)&0x3) != 0) {
        ALOGW("ResTable_package key strings at 0x%x is not on an integer boundary.",
             dtohl(pkg->keyStrings));
        return (mError=BAD_TYPE);
    }

    uint32_t id = dtohl(pkg->id);
    KeyedVector<uint8_t, IdmapEntries> idmapEntries;

    if (header->resourceIDMap != NULL) {
        uint8_t targetPackageId = 0;
        status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries);
        if (err != NO_ERROR) {
            ALOGW("Overlay is broken");
            return (mError=err);
        }
        id = targetPackageId;
    }

    if (id >= 256) {
        LOG_ALWAYS_FATAL("Package id out of range");
        return NO_ERROR;
    } else if (id == 0) {
        // This is a library so assign an ID
        id = mNextPackageId++;
    }

    PackageGroup* group = NULL;
    Package* package = new Package(this, header, pkg);
    if (package == NULL) {
        return (mError=NO_MEMORY);
    }

    err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
                                   header->dataEnd-(base+dtohl(pkg->typeStrings)));
    if (err != NO_ERROR) {
        delete group;
        delete package;
        return (mError=err);
    }

    err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
                                  header->dataEnd-(base+dtohl(pkg->keyStrings)));
    if (err != NO_ERROR) {
        delete group;
        delete package;
        return (mError=err);
    }

    size_t idx = mPackageMap[id];
    if (idx == 0) {
        idx = mPackageGroups.size() + 1;

        char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
        strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
        group = new PackageGroup(this, String16(tmpName), id);
        if (group == NULL) {
            delete package;
            return (mError=NO_MEMORY);
        }

        err = mPackageGroups.add(group);
        if (err < NO_ERROR) {
            return (mError=err);
        }

        mPackageMap[id] = static_cast<uint8_t>(idx);

        // Find all packages that reference this package
        size_t N = mPackageGroups.size();
        for (size_t i = 0; i < N; i++) {
            mPackageGroups[i]->dynamicRefTable.addMapping(
                    group->name, static_cast<uint8_t>(group->id));
        }
    } else {
        group = mPackageGroups.itemAt(idx - 1);
        if (group == NULL) {
            return (mError=UNKNOWN_ERROR);
        }
    }

    err = group->packages.add(package);
    if (err < NO_ERROR) {
        return (mError=err);
    }

    // Iterate through all chunks.
    const ResChunk_header* chunk =
        (const ResChunk_header*)(((const uint8_t*)pkg)
                                 + dtohs(pkg->header.headerSize));
    const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size);
    while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) &&
           ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) {
        if (kDebugTableNoisy) {
            ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n",
                    dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size),
                    (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)));
        }
        const size_t csize = dtohl(chunk->size);
        const uint16_t ctype = dtohs(chunk->type);
        if (ctype == RES_TABLE_TYPE_SPEC_TYPE) {
            const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk);
            err = validate_chunk(&typeSpec->header, sizeof(*typeSpec),
                                 endPos, "ResTable_typeSpec");
            if (err != NO_ERROR) {
                return (mError=err);
            }

            const size_t typeSpecSize = dtohl(typeSpec->header.size);
            const size_t newEntryCount = dtohl(typeSpec->entryCount);

            if (kDebugLoadTableNoisy) {
                ALOGI("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
                        (void*)(base-(const uint8_t*)chunk),
                        dtohs(typeSpec->header.type),
                        dtohs(typeSpec->header.headerSize),
                        (void*)typeSpecSize);
            }
            // look for block overrun or int overflow when multiplying by 4
            if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
                    || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
                    > typeSpecSize)) {
                ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
                        (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
                        (void*)typeSpecSize);
                return (mError=BAD_TYPE);
            }

            if (typeSpec->id == 0) {
                ALOGW("ResTable_type has an id of 0.");
                return (mError=BAD_TYPE);
            }

            if (newEntryCount > 0) {
                uint8_t typeIndex = typeSpec->id - 1;
                ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id);
                if (idmapIndex >= 0) {
                    typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
                }

                TypeList& typeList = group->types.editItemAt(typeIndex);
                if (!typeList.isEmpty()) {
                    const Type* existingType = typeList[0];
                    if (existingType->entryCount != newEntryCount && idmapIndex < 0) {
                        ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
                                (int) newEntryCount, (int) existingType->entryCount);
                        // We should normally abort here, but some legacy apps declare
                        // resources in the 'android' package (old bug in AAPT).
                    }
                }

                Type* t = new Type(header, package, newEntryCount);
                t->typeSpec = typeSpec;
                t->typeSpecFlags = (const uint32_t*)(
                        ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
                if (idmapIndex >= 0) {
                    t->idmapEntries = idmapEntries[idmapIndex];
                }
                typeList.add(t);
                group->largestTypeId = max(group->largestTypeId, typeSpec->id);
            } else {
                ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id);
            }

        } else if (ctype == RES_TABLE_TYPE_TYPE) {
            const ResTable_type* type = (const ResTable_type*)(chunk);
            err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4,
                                 endPos, "ResTable_type");
            if (err != NO_ERROR) {
                return (mError=err);
            }

            const uint32_t typeSize = dtohl(type->header.size);
            const size_t newEntryCount = dtohl(type->entryCount);

            if (kDebugLoadTableNoisy) {
                printf("Type off %p: type=0x%x, headerSize=0x%x, size=%u\n",
                        (void*)(base-(const uint8_t*)chunk),
                        dtohs(type->header.type),
                        dtohs(type->header.headerSize),
                        typeSize);
            }
            if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount) > typeSize) {
                ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
                        (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
                        typeSize);
                return (mError=BAD_TYPE);
            }

            if (newEntryCount != 0
                && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
                ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
                     dtohl(type->entriesStart), typeSize);
                return (mError=BAD_TYPE);
            }

            if (type->id == 0) {
                ALOGW("ResTable_type has an id of 0.");
                return (mError=BAD_TYPE);
            }

            if (newEntryCount > 0) {
                uint8_t typeIndex = type->id - 1;
                ssize_t idmapIndex = idmapEntries.indexOfKey(type->id);
                if (idmapIndex >= 0) {
                    typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
                }

                TypeList& typeList = group->types.editItemAt(typeIndex);
                if (typeList.isEmpty()) {
                    ALOGE("No TypeSpec for type %d", type->id);
                    return (mError=BAD_TYPE);
                }

                Type* t = typeList.editItemAt(typeList.size() - 1);
                if (newEntryCount != t->entryCount) {
                    ALOGE("ResTable_type entry count inconsistent: given %d, previously %d",
                        (int)newEntryCount, (int)t->entryCount);
                    return (mError=BAD_TYPE);
                }

                if (t->package != package) {
                    ALOGE("No TypeSpec for type %d", type->id);
                    return (mError=BAD_TYPE);
                }

                t->configs.add(type);

                if (kDebugTableGetEntry) {
                    ResTable_config thisConfig;
                    thisConfig.copyFromDtoH(type->config);
                    ALOGI("Adding config to type %d: %s\n", type->id,
                            thisConfig.toString().string());
                }
            } else {
                ALOGV("Skipping empty ResTable_type for type %d", type->id);
            }

        } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
            if (group->dynamicRefTable.entries().size() == 0) {
                status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk);
                if (err != NO_ERROR) {
                    return (mError=err);
                }

                // Fill in the reference table with the entries we already know about.
                size_t N = mPackageGroups.size();
                for (size_t i = 0; i < N; i++) {
                    group->dynamicRefTable.addMapping(mPackageGroups[i]->name, mPackageGroups[i]->id);
                }
            } else {
                ALOGW("Found multiple library tables, ignoring...");
            }
        } else {
            status_t err = validate_chunk(chunk, sizeof(ResChunk_header),
                                          endPos, "ResTable_package:unknown");
            if (err != NO_ERROR) {
                return (mError=err);
            }
        }
        chunk = (const ResChunk_header*)
            (((const uint8_t*)chunk) + csize);
    }

    return NO_ERROR;
}