diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/androidfw/AssetManager2.cpp | 38 | ||||
| -rw-r--r-- | libs/androidfw/LoadedArsc.cpp | 24 | ||||
| -rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 46 | ||||
| -rw-r--r-- | libs/androidfw/TypeWrappers.cpp | 6 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/LoadedArsc.h | 8 | ||||
| -rw-r--r-- | libs/androidfw/include/androidfw/ResourceTypes.h | 93 | ||||
| -rw-r--r-- | libs/androidfw/tests/TypeWrappers_test.cpp | 12 |
7 files changed, 142 insertions, 85 deletions
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 1381bdd6a50d..06ffb72d183b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -43,28 +43,19 @@ namespace { using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>; +/* NOTE: table_entry has been verified in LoadedPackage::GetEntryFromOffset(), + * and so access to ->value() and ->map_entry() are safe here + */ base::expected<EntryValue, IOError> GetEntryValue( incfs::verified_map_ptr<ResTable_entry> table_entry) { - const uint16_t entry_size = dtohs(table_entry->size); + const uint16_t entry_size = table_entry->size(); // Check if the entry represents a bag value. - if (entry_size >= sizeof(ResTable_map_entry) && - (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { - const auto map_entry = table_entry.convert<ResTable_map_entry>(); - if (!map_entry) { - return base::unexpected(IOError::PAGES_MISSING); - } - return map_entry.verified(); + if (entry_size >= sizeof(ResTable_map_entry) && table_entry->is_complex()) { + return table_entry.convert<ResTable_map_entry>().verified(); } - // The entry represents a non-bag value. - const auto entry_value = table_entry.offset(entry_size).convert<Res_value>(); - if (!entry_value) { - return base::unexpected(IOError::PAGES_MISSING); - } - Res_value value; - value.copyFrom_dtoh(entry_value.value()); - return value; + return table_entry->value(); } } // namespace @@ -814,17 +805,12 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( return base::unexpected(std::nullopt); } - auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (!best_entry_result.has_value()) { - return base::unexpected(best_entry_result.error()); - } - - const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result; - if (!best_entry) { - return base::unexpected(IOError::PAGES_MISSING); + auto best_entry_verified = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (!best_entry_verified.has_value()) { + return base::unexpected(best_entry_verified.error()); } - const auto entry = GetEntryValue(best_entry.verified()); + const auto entry = GetEntryValue(*best_entry_verified); if (!entry.has_value()) { return base::unexpected(entry.error()); } @@ -837,7 +823,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( .package_name = &best_package->GetPackageName(), .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1), .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(), - best_entry->key.index), + (*best_entry_verified)->key()), .dynamic_ref_table = package_group.dynamic_ref_table.get(), }; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 5b69cca2d747..e78f91ee3f46 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -107,8 +107,8 @@ static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) { return true; } -static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry( - incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) { +static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> +VerifyResTableEntry(incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) { // Check that the offset is aligned. if (UNLIKELY(entry_offset & 0x03U)) { LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; @@ -136,7 +136,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry( return base::unexpected(IOError::PAGES_MISSING); } - const size_t entry_size = dtohs(entry->size); + const size_t entry_size = entry->size(); if (UNLIKELY(entry_size < sizeof(entry.value()))) { LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; @@ -149,6 +149,11 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry( return base::unexpected(std::nullopt); } + // If entry is compact, value is already encoded, and a compact entry + // cannot be a map_entry, we are done verifying + if (entry->is_compact()) + return entry.verified(); + if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) { @@ -192,7 +197,7 @@ static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry( return base::unexpected(std::nullopt); } } - return {}; + return entry.verified(); } LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei) @@ -228,7 +233,7 @@ uint32_t LoadedPackage::iterator::operator*() const { entryIndex_); } -base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry( +base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry( incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) { base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index); if (UNLIKELY(!entry_offset.has_value())) { @@ -297,13 +302,14 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset( return value; } -base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) { +base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> +LoadedPackage::GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk, + uint32_t offset) { auto valid = VerifyResTableEntry(type_chunk, offset); if (UNLIKELY(!valid.has_value())) { return base::unexpected(valid.error()); } - return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>(); + return valid; } base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations( @@ -400,7 +406,7 @@ base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName( return base::unexpected(IOError::PAGES_MISSING); } - if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) { + if (entry->key() == static_cast<uint32_t>(*key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 46fbe7c4d981..aac52b46f909 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -4415,20 +4415,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag return err; } - if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) { + if (entry.entry->map_entry()) { if (!mayBeBag) { ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID); } return BAD_VALUE; } - const Res_value* value = reinterpret_cast<const Res_value*>( - reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size); - - outValue->size = dtohs(value->size); - outValue->res0 = value->res0; - outValue->dataType = value->dataType; - outValue->data = dtohl(value->data); + *outValue = entry.entry->value(); // The reference may be pointing to a resource in a shared library. These // references have build-time generated package IDs. These ids may not match @@ -4619,11 +4613,10 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, return err; } - const uint16_t entrySize = dtohs(entry.entry->size); - const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0; - const uint32_t count = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0; + const uint16_t entrySize = entry.entry->size(); + const ResTable_map_entry* map_entry = entry.entry->map_entry(); + const uint32_t parent = map_entry ? dtohl(map_entry->parent.ident) : 0; + const uint32_t count = map_entry ? dtohl(map_entry->count) : 0; size_t N = count; @@ -4687,7 +4680,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, // Now merge in the new attributes... size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type)) - + dtohs(entry.entry->size); + + entrySize; const ResTable_map* map; bag_entry* entries = (bag_entry*)(set+1); size_t curEntry = 0; @@ -5065,7 +5058,7 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const continue; } - if (dtohl(entry->key.index) == (size_t) *ei) { + if (entry->key() == (size_t) *ei) { uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index()); if (outTypeSpecFlags) { Entry result; @@ -6579,8 +6572,8 @@ status_t ResTable::getEntry( const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>( reinterpret_cast<const uint8_t*>(bestType) + bestOffset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); + if (entry->size() < sizeof(*entry)) { + ALOGW("ResTable_entry size 0x%zx is too small", entry->size()); return BAD_TYPE; } @@ -6591,7 +6584,7 @@ status_t ResTable::getEntry( outEntry->specFlags = specFlags; outEntry->package = bestPackage; outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset); - outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index)); + outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, entry->key()); } return NO_ERROR; } @@ -7662,7 +7655,7 @@ void ResTable::print(bool inclValues) const continue; } - uintptr_t esize = dtohs(ent->size); + uintptr_t esize = ent->size(); if ((esize&0x3) != 0) { printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize); continue; @@ -7674,30 +7667,27 @@ void ResTable::print(bool inclValues) const } const Res_value* valuePtr = NULL; - const ResTable_map_entry* bagPtr = NULL; + const ResTable_map_entry* bagPtr = ent->map_entry(); Res_value value; - if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + if (bagPtr) { printf("<bag>"); - bagPtr = (const ResTable_map_entry*)ent; } else { - valuePtr = (const Res_value*) - (((const uint8_t*)ent) + esize); - value.copyFrom_dtoh(*valuePtr); + value = ent->value(); printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", (int)value.dataType, (int)value.data, (int)value.size, (int)value.res0); } - if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + if (ent->flags() & ResTable_entry::FLAG_PUBLIC) { printf(" (PUBLIC)"); } printf("\n"); if (inclValues) { - if (valuePtr != NULL) { + if (bagPtr == NULL) { printf(" "); print_value(typeConfigs->package, value); - } else if (bagPtr != NULL) { + } else { const int N = dtohl(bagPtr->count); const uint8_t* baseMapPtr = (const uint8_t*)ent; size_t mapOffset = esize; diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp index 647aa197a94d..2c39a81aca3e 100644 --- a/libs/androidfw/TypeWrappers.cpp +++ b/libs/androidfw/TypeWrappers.cpp @@ -91,11 +91,11 @@ const ResTable_entry* TypeVariant::iterator::operator*() const { if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) { ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex); return NULL; - } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) { + } else if (reinterpret_cast<uintptr_t>(entry) + entry->size() > containerEnd) { ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex); return NULL; - } else if (dtohs(entry->size) < sizeof(*entry)) { - ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size)); + } else if (entry->size() < sizeof(*entry)) { + ALOGE("Entry at index %u is too small (%zu)", mIndex, entry->size()); return NULL; } return entry; diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index e45963950b04..79d962829046 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -166,14 +166,14 @@ class LoadedPackage { base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); + static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> + GetEntry(incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); static base::expected<uint32_t, NullOrIOError> GetEntryOffset( incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); - static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset); + static base::expected<incfs::verified_map_ptr<ResTable_entry>, NullOrIOError> + GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset); // Returns the string pool where type names are stored. const ResStringPool* GetTypeStringPool() const { diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 24628cd36ba5..42d8cbeb8dfd 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -26,6 +26,7 @@ #include <androidfw/Errors.h> #include <androidfw/LocaleData.h> #include <androidfw/StringPiece.h> +#include <utils/ByteOrder.h> #include <utils/Errors.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -1490,6 +1491,8 @@ union ResTable_sparseTypeEntry { static_assert(sizeof(ResTable_sparseTypeEntry) == sizeof(uint32_t), "ResTable_sparseTypeEntry must be 4 bytes in size"); +struct ResTable_map_entry; + /** * This is the beginning of information about an entry in the resource * table. It holds the reference to the name of this entry, and is @@ -1497,12 +1500,11 @@ static_assert(sizeof(ResTable_sparseTypeEntry) == sizeof(uint32_t), * * A Res_value structure, if FLAG_COMPLEX is -not- set. * * An array of ResTable_map structures, if FLAG_COMPLEX is set. * These supply a set of name/value mappings of data. + * * If FLAG_COMPACT is set, this entry is a compact entry for + * simple values only */ -struct ResTable_entry +union ResTable_entry { - // Number of bytes in this structure. - uint16_t size; - enum { // If set, this is a complex entry, holding a set of name/value // mappings. It is followed by an array of ResTable_map structures. @@ -1514,18 +1516,91 @@ struct ResTable_entry // resources of the same name/type. This is only useful during // linking with other resource tables. FLAG_WEAK = 0x0004, + // If set, this is a compact entry with data type and value directly + // encoded in the this entry, see ResTable_entry::compact + FLAG_COMPACT = 0x0008, }; - uint16_t flags; - - // Reference into ResTable_package::keyStrings identifying this entry. - struct ResStringPool_ref key; + + struct Full { + // Number of bytes in this structure. + uint16_t size; + + uint16_t flags; + + // Reference into ResTable_package::keyStrings identifying this entry. + struct ResStringPool_ref key; + } full; + + /* A compact entry is indicated by FLAG_COMPACT, with flags at the same + * offset as a normal entry. This is only for simple data values where + * + * - size for entry or value can be inferred (both being 8 bytes). + * - key index is encoded in 16-bit + * - dataType is encoded as the higher 8-bit of flags + * - data is encoded directly in this entry + */ + struct Compact { + uint16_t key; + uint16_t flags; + uint32_t data; + } compact; + + uint16_t flags() const { return dtohs(full.flags); }; + bool is_compact() const { return flags() & FLAG_COMPACT; } + bool is_complex() const { return flags() & FLAG_COMPLEX; } + + size_t size() const { + return is_compact() ? sizeof(ResTable_entry) : dtohs(this->full.size); + } + + uint32_t key() const { + return is_compact() ? dtohs(this->compact.key) : dtohl(this->full.key.index); + } + + /* Always verify the memory associated with this entry and its value + * before calling value() or map_entry() + */ + Res_value value() const { + Res_value v; + if (is_compact()) { + v.size = sizeof(Res_value); + v.res0 = 0; + v.data = dtohl(this->compact.data); + v.dataType = dtohs(compact.flags) >> 8; + } else { + auto vaddr = reinterpret_cast<const uint8_t*>(this) + dtohs(this->full.size); + auto value = reinterpret_cast<const Res_value*>(vaddr); + v.size = dtohs(value->size); + v.res0 = value->res0; + v.data = dtohl(value->data); + v.dataType = value->dataType; + } + return v; + } + + const ResTable_map_entry* map_entry() const { + return is_complex() && !is_compact() ? + reinterpret_cast<const ResTable_map_entry*>(this) : nullptr; + } }; +/* Make sure size of ResTable_entry::Full and ResTable_entry::Compact + * be the same as ResTable_entry. This is to allow iteration of entries + * to work in either cases. + * + * The offset of flags must be at the same place for both structures, + * to ensure the correct reading to decide whether this is a full entry + * or a compact entry. + */ +static_assert(sizeof(ResTable_entry) == sizeof(ResTable_entry::Full)); +static_assert(sizeof(ResTable_entry) == sizeof(ResTable_entry::Compact)); +static_assert(offsetof(ResTable_entry, full.flags) == offsetof(ResTable_entry, compact.flags)); + /** * Extended form of a ResTable_entry for map entries, defining a parent map * resource from which to inherit values. */ -struct ResTable_map_entry : public ResTable_entry +struct ResTable_map_entry : public ResTable_entry::Full { // Resource identifier of the parent mapping, or 0 if there is none. // This is always treated as a TYPE_DYNAMIC_REFERENCE. diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp index d69abe5d0f11..aba3ab3a06a2 100644 --- a/libs/androidfw/tests/TypeWrappers_test.cpp +++ b/libs/androidfw/tests/TypeWrappers_test.cpp @@ -37,8 +37,8 @@ void* createTypeData() { offsets[0] = 0; ResTable_entry e1; memset(&e1, 0, sizeof(e1)); - e1.size = sizeof(e1); - e1.key.index = 0; + e1.full.size = sizeof(e1); + e1.full.key.index = 0; t.header.size += sizeof(e1); Res_value v1; @@ -50,8 +50,8 @@ void* createTypeData() { offsets[2] = sizeof(e1) + sizeof(v1); ResTable_entry e2; memset(&e2, 0, sizeof(e2)); - e2.size = sizeof(e2); - e2.key.index = 1; + e2.full.size = sizeof(e2); + e2.full.key.index = 1; t.header.size += sizeof(e2); Res_value v2; @@ -83,7 +83,7 @@ TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) { TypeVariant::iterator iter = v.beginEntries(); ASSERT_EQ(uint32_t(0), iter.index()); ASSERT_TRUE(NULL != *iter); - ASSERT_EQ(uint32_t(0), iter->key.index); + ASSERT_EQ(uint32_t(0), iter->full.key.index); ASSERT_NE(v.endEntries(), iter); iter++; @@ -96,7 +96,7 @@ TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) { ASSERT_EQ(uint32_t(2), iter.index()); ASSERT_TRUE(NULL != *iter); - ASSERT_EQ(uint32_t(1), iter->key.index); + ASSERT_EQ(uint32_t(1), iter->full.key.index); ASSERT_NE(v.endEntries(), iter); iter++; |