AssetManager2: Run ApkAssets that have failed verification
ApkAssets who have failed verification should still run for
compatibility. Not all resources are accessed, and therefore
errors in the APK are not necessarily fatal. However, this means
we must do bounds checks when retrieving resources, which is
slower.
Test: make libandroidfw_tests && $ANDROID_BUILD_TOP/out/host/<host>/nativetest64/libandroidfw_tests/libandroidfw_tests
Test: make libandroidfw_benchmarks && adb sync system && adb sync data && /data/benchmarktest64/libandroidfw_benchmarks/libandroidfw_benchmarks
Change-Id: I4cc926c064bca0491785d82cdac0419d74d7d9b0
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index c47db69..83c82af 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -264,9 +264,7 @@
}
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
- bool stop_at_first_match, LoadedArscEntry* out_entry,
- ResTable_config* out_selected_config,
- uint32_t* out_flags) {
+ bool stop_at_first_match, FindEntryResult* out_entry) {
ATRACE_CALL();
// Might use this if density_override != 0.
@@ -295,30 +293,28 @@
return kInvalidCookie;
}
- LoadedArscEntry best_entry;
- ResTable_config best_config;
+ FindEntryResult best_entry;
ApkAssetsCookie best_cookie = kInvalidCookie;
uint32_t cumulated_flags = 0u;
const PackageGroup& package_group = package_groups_[idx];
const size_t package_count = package_group.packages_.size();
for (size_t i = 0; i < package_count; i++) {
- LoadedArscEntry current_entry;
- ResTable_config current_config;
- uint32_t current_flags = 0;
-
const LoadedPackage* loaded_package = package_group.packages_[i];
- if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry,
- ¤t_config, ¤t_flags)) {
+
+ FindEntryResult current_entry;
+ if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) {
continue;
}
- cumulated_flags |= current_flags;
+ cumulated_flags |= current_entry.type_flags;
- if (best_cookie == kInvalidCookie || current_config.isBetterThan(best_config, desired_config) ||
- (loaded_package->IsOverlay() && current_config.compare(best_config) == 0)) {
+ const ResTable_config* current_config = current_entry.config;
+ const ResTable_config* best_config = best_entry.config;
+ if (best_cookie == kInvalidCookie ||
+ current_config->isBetterThan(*best_config, desired_config) ||
+ (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) {
best_entry = current_entry;
- best_config = current_config;
best_cookie = package_group.cookies_[i];
if (stop_at_first_match) {
break;
@@ -332,19 +328,16 @@
*out_entry = best_entry;
out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
- *out_selected_config = best_config;
- *out_flags = cumulated_flags;
+ out_entry->type_flags = cumulated_flags;
return best_cookie;
}
bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
ATRACE_CALL();
- LoadedArscEntry entry;
- ResTable_config config;
- uint32_t flags = 0u;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- true /* stop_at_first_match */, &entry, &config, &flags);
+ FindEntryResult entry;
+ ApkAssetsCookie cookie =
+ FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry);
if (cookie == kInvalidCookie) {
return false;
}
@@ -378,11 +371,14 @@
}
bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
- LoadedArscEntry entry;
- ResTable_config config;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- false /* stop_at_first_match */, &entry, &config, out_flags);
- return cookie != kInvalidCookie;
+ FindEntryResult entry;
+ ApkAssetsCookie cookie =
+ FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
+ if (cookie != kInvalidCookie) {
+ *out_flags = entry.type_flags;
+ return cookie;
+ }
+ return kInvalidCookie;
}
ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
@@ -391,11 +387,9 @@
uint32_t* out_flags) {
ATRACE_CALL();
- LoadedArscEntry entry;
- ResTable_config config;
- uint32_t flags = 0u;
+ FindEntryResult entry;
ApkAssetsCookie cookie =
- FindEntry(resid, density_override, false /* stop_at_first_match */, &entry, &config, &flags);
+ FindEntry(resid, density_override, false /* stop_at_first_match */, &entry);
if (cookie == kInvalidCookie) {
return kInvalidCookie;
}
@@ -409,8 +403,8 @@
// Create a reference since we can't represent this complex type as a Res_value.
out_value->dataType = Res_value::TYPE_REFERENCE;
out_value->data = resid;
- *out_selected_config = config;
- *out_flags = flags;
+ *out_selected_config = *entry.config;
+ *out_flags = entry.type_flags;
return cookie;
}
@@ -421,8 +415,8 @@
// Convert the package ID to the runtime assigned package ID.
entry.dynamic_ref_table->lookupResourceValue(out_value);
- *out_selected_config = config;
- *out_flags = flags;
+ *out_selected_config = *entry.config;
+ *out_flags = entry.type_flags;
return cookie;
}
@@ -465,11 +459,9 @@
return cached_iter->second.get();
}
- LoadedArscEntry entry;
- ResTable_config config;
- uint32_t flags = 0u;
- ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */,
- false /* stop_at_first_match */, &entry, &config, &flags);
+ FindEntryResult entry;
+ ApkAssetsCookie cookie =
+ FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
if (cookie == kInvalidCookie) {
return nullptr;
}
@@ -520,7 +512,7 @@
}
++new_entry;
}
- new_bag->type_spec_flags = flags;
+ new_bag->type_spec_flags = entry.type_flags;
new_bag->entry_count = static_cast<uint32_t>(entry_count);
ResolvedBag* result = new_bag.get();
cached_bags_[resid] = std::move(new_bag);
@@ -538,9 +530,6 @@
return nullptr;
}
- // Combine flags from the parent and our own bag.
- flags |= parent_bag->type_spec_flags;
-
// Create the max possible entries we can make. Once we construct the bag,
// we will realloc to fit to size.
const size_t max_count = parent_bag->entry_count + dtohl(map->count);
@@ -629,7 +618,8 @@
new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
}
- new_bag->type_spec_flags = flags;
+ // Combine flags from the parent and our own bag.
+ new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags;
new_bag->entry_count = static_cast<uint32_t>(actual_count);
ResolvedBag* result = new_bag.get();
cached_bags_[resid] = std::move(new_bag);
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index b62fc81..c361ea2 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -122,9 +122,180 @@
} // namespace
+LoadedPackage::LoadedPackage() = default;
+LoadedPackage::~LoadedPackage() = default;
+
+// Precondition: The header passed in has already been verified, so reading any fields and trusting
+// the ResChunk_header is safe.
+static bool VerifyResTableType(const ResTable_type* header) {
+ const size_t entry_count = dtohl(header->entryCount);
+ if (entry_count > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE.";
+ return false;
+ }
+
+ // Make sure that there is enough room for the entry offsets.
+ const size_t offsets_offset = dtohs(header->header.headerSize);
+ const size_t entries_offset = dtohl(header->entriesStart);
+ const size_t offsets_length = sizeof(uint32_t) * entry_count;
+
+ if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
+ LOG(ERROR) << "Entry offsets overlap actual entry data.";
+ return false;
+ }
+
+ if (entries_offset > dtohl(header->header.size)) {
+ LOG(ERROR) << "Entry offsets extend beyond chunk.";
+ return false;
+ }
+
+ if (entries_offset & 0x03) {
+ LOG(ERROR) << "Entries start at unaligned address.";
+ return false;
+ }
+ return true;
+}
+
+static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset,
+ size_t entry_idx) {
+ // Check that the offset is aligned.
+ if (entry_offset & 0x03) {
+ LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned.";
+ return false;
+ }
+
+ // Check that the offset doesn't overflow.
+ if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) {
+ // Overflow in offset.
+ LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large.";
+ return false;
+ }
+
+ const size_t chunk_size = dtohl(type->header.size);
+
+ entry_offset += dtohl(type->entriesStart);
+ if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
+ LOG(ERROR) << "Entry offset at index " << entry_idx
+ << " is too large. No room for ResTable_entry.";
+ return false;
+ }
+
+ const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(type) + entry_offset);
+
+ const size_t entry_size = dtohs(entry->size);
+ if (entry_size < sizeof(*entry)) {
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
+ << " is too small.";
+ return false;
+ }
+
+ if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
+ << " is too large.";
+ return false;
+ }
+
+ if (entry_size < sizeof(ResTable_map_entry)) {
+ // There needs to be room for one Res_value struct.
+ if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) {
+ LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx
+ << " for type " << (int)type->id << ".";
+ return false;
+ }
+
+ const Res_value* value =
+ reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size);
+ const size_t value_size = dtohs(value->size);
+ if (value_size < sizeof(Res_value)) {
+ LOG(ERROR) << "Res_value at index " << entry_idx << " is too small.";
+ return false;
+ }
+
+ if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
+ LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx
+ << " is too large.";
+ return false;
+ }
+ } else {
+ const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
+ const size_t map_entry_count = dtohl(map->count);
+ size_t map_entries_start = entry_offset + entry_size;
+ if (map_entries_start & 0x03) {
+ LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset.";
+ return false;
+ }
+
+ // Each entry is sizeof(ResTable_map) big.
+ if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) {
+ LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << ".";
+ return false;
+ }
+ }
+ return true;
+}
+
+template <bool Verified>
+bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx,
+ const ResTable_config& config, FindEntryResult* out_entry) const {
+ const ResTable_config* best_config = nullptr;
+ const ResTable_type* best_type = nullptr;
+ uint32_t best_offset = 0;
+
+ for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) {
+ const Type* type = &type_spec_ptr->types[i];
+
+ if (type->configuration.match(config) &&
+ (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
+ // The configuration matches and is better than the previous selection.
+ // Find the entry value if it exists for this configuration.
+ const size_t entry_count = dtohl(type->type->entryCount);
+ const size_t offsets_offset = dtohs(type->type->header.headerSize);
+ if (entry_idx < entry_count) {
+ // If the package hasn't been verified, do bounds checking.
+ if (!Verified) {
+ if (!VerifyResTableType(type->type)) {
+ continue;
+ }
+ }
+
+ const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<const uint8_t*>(type->type) + offsets_offset);
+ const uint32_t offset = dtohl(entry_offsets[entry_idx]);
+ if (offset != ResTable_type::NO_ENTRY) {
+ // There is an entry for this resource, record it.
+ best_config = &type->configuration;
+ best_type = type->type;
+ best_offset = offset;
+ }
+ }
+ }
+ }
+
+ if (best_type == nullptr) {
+ return false;
+ }
+
+ if (!Verified) {
+ if (!VerifyResTableEntry(best_type, best_offset, entry_idx)) {
+ return false;
+ }
+ }
+
+ const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(best_type) + best_offset + dtohl(best_type->entriesStart));
+
+ const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec_ptr->type_spec + 1);
+ out_entry->type_flags = dtohl(flags[entry_idx]);
+ out_entry->entry = best_entry;
+ out_entry->config = best_config;
+ out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1);
+ out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index));
+ return true;
+}
+
bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
- LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
- uint32_t* out_flags) const {
+ FindEntryResult* out_entry) const {
ATRACE_CALL();
// If the type IDs are offset in this package, we need to take that into account when searching
@@ -142,120 +313,27 @@
}
}
- // Don't bother checking if the entry ID is larger than
- // the number of entries.
+ // Don't bother checking if the entry ID is larger than the number of entries.
if (entry_idx >= dtohl(ptr->type_spec->entryCount)) {
return false;
}
- const ResTable_config* best_config = nullptr;
- const ResTable_type* best_type = nullptr;
- uint32_t best_offset = 0;
-
- for (uint32_t i = 0; i < ptr->type_count; i++) {
- const Type* type = &ptr->types[i];
-
- if (type->configuration.match(config) &&
- (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
- // The configuration matches and is better than the previous selection.
- // Find the entry value if it exists for this configuration.
- size_t entry_count = dtohl(type->type->entryCount);
- if (entry_idx < entry_count) {
- const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
- const uint32_t offset = dtohl(entry_offsets[entry_idx]);
- if (offset != ResTable_type::NO_ENTRY) {
- // There is an entry for this resource, record it.
- best_config = &type->configuration;
- best_type = type->type;
- best_offset = offset + dtohl(type->type->entriesStart);
- }
- }
- }
+ if (verified_) {
+ return FindEntry<true>(ptr, entry_idx, config, out_entry);
}
-
- if (best_type == nullptr) {
- return false;
- }
-
- const uint32_t* flags = reinterpret_cast<const uint32_t*>(ptr->type_spec + 1);
- *out_flags = dtohl(flags[entry_idx]);
- *out_selected_config = *best_config;
-
- const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(best_type) + best_offset);
- out_entry->entry = best_entry;
- out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1);
- out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index));
- return true;
-}
-
-// The destructor gets generated into arbitrary translation units
-// if left implicit, which causes the compiler to complain about
-// forward declarations and incomplete types.
-LoadedArsc::~LoadedArsc() {}
-
-bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config,
- LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
- uint32_t* out_flags) const {
- ATRACE_CALL();
- const uint8_t package_id = get_package_id(resid);
- const uint8_t type_id = get_type_id(resid);
- const uint16_t entry_id = get_entry_id(resid);
-
- if (type_id == 0) {
- LOG(ERROR) << "Invalid ID 0x" << std::hex << resid << std::dec << ".";
- return false;
- }
-
- for (const auto& loaded_package : packages_) {
- if (loaded_package->package_id_ == package_id) {
- return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry,
- out_selected_config, out_flags);
- }
- }
- return false;
-}
-
-const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const {
- const uint8_t package_id = get_package_id(resid);
- for (const auto& loaded_package : packages_) {
- if (loaded_package->package_id_ == package_id) {
- return loaded_package.get();
- }
- }
- return nullptr;
+ return FindEntry<false>(ptr, entry_idx, config, out_entry);
}
static bool VerifyType(const Chunk& chunk) {
ATRACE_CALL();
const ResTable_type* header = chunk.header<ResTable_type, kResTableTypeMinSize>();
+ if (!VerifyResTableType(header)) {
+ return false;
+ }
+
const size_t entry_count = dtohl(header->entryCount);
- if (entry_count > std::numeric_limits<uint16_t>::max()) {
- LOG(ERROR) << "Too many entries in RES_TABLE_TYPE_TYPE.";
- return false;
- }
-
- // Make sure that there is enough room for the entry offsets.
const size_t offsets_offset = chunk.header_size();
- const size_t entries_offset = dtohl(header->entriesStart);
- const size_t offsets_length = sizeof(uint32_t) * entry_count;
-
- if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
- LOG(ERROR) << "Entry offsets overlap actual entry data.";
- return false;
- }
-
- if (entries_offset > chunk.size()) {
- LOG(ERROR) << "Entry offsets extend beyond chunk.";
- return false;
- }
-
- if (entries_offset & 0x03) {
- LOG(ERROR) << "Entries start at unaligned address.";
- return false;
- }
// Check each entry offset.
const uint32_t* offsets =
@@ -263,79 +341,9 @@
for (size_t i = 0; i < entry_count; i++) {
uint32_t offset = dtohl(offsets[i]);
if (offset != ResTable_type::NO_ENTRY) {
- // Check that the offset is aligned.
- if (offset & 0x03) {
- LOG(ERROR) << "Entry offset at index " << i << " is not 4-byte aligned.";
+ if (!VerifyResTableEntry(header, offset, i)) {
return false;
}
-
- // Check that the offset doesn't overflow.
- if (offset > std::numeric_limits<uint32_t>::max() - entries_offset) {
- // Overflow in offset.
- LOG(ERROR) << "Entry offset at index " << i << " is too large.";
- return false;
- }
-
- offset += entries_offset;
- if (offset > chunk.size() - sizeof(ResTable_entry)) {
- LOG(ERROR) << "Entry offset at index " << i << " is too large. No room for ResTable_entry.";
- return false;
- }
-
- const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(header) + offset);
- const size_t entry_size = dtohs(entry->size);
- if (entry_size < sizeof(*entry)) {
- LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << i << " is too small.";
- return false;
- }
-
- // Check the declared entrySize.
- if (entry_size > chunk.size() || offset > chunk.size() - entry_size) {
- LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << i << " is too large.";
- return false;
- }
-
- // If this is a map entry, then keep validating.
- if (entry_size >= sizeof(ResTable_map_entry)) {
- const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry);
- const size_t map_entry_count = dtohl(map->count);
-
- size_t map_entries_start = offset + entry_size;
- if (map_entries_start & 0x03) {
- LOG(ERROR) << "Map entries at index " << i << " start at unaligned offset.";
- return false;
- }
-
- // Each entry is sizeof(ResTable_map) big.
- if (map_entry_count > ((chunk.size() - map_entries_start) / sizeof(ResTable_map))) {
- LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << i << ".";
- return false;
- }
-
- // Great, all the map entries fit!.
- } else {
- // There needs to be room for one Res_value struct.
- if (offset + entry_size > chunk.size() - sizeof(Res_value)) {
- LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << i << " for type "
- << (int)header->id << " with config " << header->config.toString().string()
- << ".";
- return false;
- }
-
- const Res_value* value = reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(entry) + entry_size);
- const size_t value_size = dtohs(value->size);
- if (value_size < sizeof(Res_value)) {
- LOG(ERROR) << "Res_value at index " << i << " is too small.";
- return false;
- }
-
- if (value_size > chunk.size() || offset + entry_size > chunk.size() - value_size) {
- LOG(ERROR) << "Res_value size " << value_size << " at index " << i << " is too large.";
- return false;
- }
- }
}
}
return true;
@@ -431,10 +439,21 @@
return 0u;
}
-std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
- const LoadedIdmap* loaded_idmap) {
+const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const {
+ const uint8_t package_id = get_package_id(resid);
+ for (const auto& loaded_package : packages_) {
+ if (loaded_package->GetPackageId() == package_id) {
+ return loaded_package.get();
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
+ const LoadedIdmap* loaded_idmap,
+ bool system, bool load_as_shared_library) {
ATRACE_CALL();
- std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()};
+ std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
// typeIdOffset was added at some point, but we still must recognize apps built before this
// was added.
@@ -446,8 +465,11 @@
return {};
}
+ loaded_package->system_ = system;
+
loaded_package->package_id_ = dtohl(header->id);
- if (loaded_package->package_id_ == 0) {
+ if (loaded_package->package_id_ == 0 ||
+ (loaded_package->package_id_ == kAppPackageId && load_as_shared_library)) {
// Package ID of 0 means this is a shared library.
loaded_package->dynamic_ = true;
}
@@ -593,13 +615,16 @@
// Type chunks must be preceded by their TypeSpec chunks.
if (!types_builder || type->id - 1 != last_type_idx) {
- LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without "
- "RES_TABLE_TYPE_SPEC_TYPE.";
+ LOG(ERROR) << "Found RES_TABLE_TYPE_TYPE chunk without RES_TABLE_TYPE_SPEC_TYPE.";
return {};
}
- if (!VerifyType(child_chunk)) {
- return {};
+ // Only verify the type if we haven't already failed verification.
+ if (loaded_package->verified_) {
+ if (!VerifyType(child_chunk)) {
+ LOG(WARNING) << "Package failed verification, resource retrieval may be slower";
+ loaded_package->verified_ = false;
+ }
}
types_builder->AddType(type);
@@ -669,7 +694,28 @@
LOG(ERROR) << iter.GetLastError();
return {};
}
- return loaded_package;
+ return std::move(loaded_package);
+}
+
+bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config,
+ FindEntryResult* out_entry) const {
+ ATRACE_CALL();
+
+ const uint8_t package_id = get_package_id(resid);
+ const uint8_t type_id = get_type_id(resid);
+ const uint16_t entry_id = get_entry_id(resid);
+
+ if (type_id == 0) {
+ LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
+ return false;
+ }
+
+ for (const auto& loaded_package : packages_) {
+ if (loaded_package->GetPackageId() == package_id) {
+ return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry);
+ }
+ }
+ return false;
}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
@@ -712,17 +758,11 @@
}
packages_seen++;
- std::unique_ptr<LoadedPackage> loaded_package =
- LoadedPackage::Load(child_chunk, loaded_idmap);
+ std::unique_ptr<const LoadedPackage> loaded_package =
+ LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library);
if (!loaded_package) {
return false;
}
-
- // Mark the package as dynamic if we are forcefully loading the Apk as a shared library.
- if (loaded_package->package_id_ == kAppPackageId) {
- loaded_package->dynamic_ = load_as_shared_library;
- }
- loaded_package->system_ = system_;
packages_.push_back(std::move(loaded_package));
} break;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7a0ef2b..87999c3 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -6014,9 +6014,6 @@
StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
: mPool(pool), mIndex(index) {}
-StringPoolRef::StringPoolRef()
- : mPool(NULL), mIndex(0) {}
-
const char* StringPoolRef::string8(size_t* outLen) const {
if (mPool != NULL) {
return mPool->string8At(mIndex, outLen);
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 2dd8125..a77c4b9 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -245,21 +245,22 @@
private:
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
- // Finds the best entry for `resid` amongst all the ApkAssets. The entry can be a simple
- // Res_value, or a complex map/bag type.
+ // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
+ // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
+ // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
+ // the ApkAssets in which the entry was found.
//
// `density_override` overrides the density of the current configuration when doing a search.
//
// When `stop_at_first_match` is true, the first match found is selected and the search
// terminates. This is useful for methods that just look up the name of a resource and don't
- // care about the value. In this case, the value of `out_flags` is incomplete and should not
- // be used.
+ // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete
+ // and should not be used.
//
- // `out_flags` stores the resulting bitmask of configuration axis with which the resource
- // value varies.
+ // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
+ // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
- LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
- uint32_t* out_flags);
+ FindEntryResult* out_entry);
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 1f272e8..377735b 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -41,12 +41,18 @@
int package_id = 0;
};
-struct LoadedArscEntry {
+struct FindEntryResult {
// A pointer to the resource table entry for this resource.
// If the size of the entry is > sizeof(ResTable_entry), it can be cast to
// a ResTable_map_entry and processed as a bag/map.
const ResTable_entry* entry = nullptr;
+ // The configuration for which the resulting entry was defined.
+ const ResTable_config* config = nullptr;
+
+ // Stores the resulting bitmask of configuration axis with which the resource value varies.
+ uint32_t type_flags = 0u;
+
// The dynamic package ID map for the package from which this resource came from.
const DynamicRefTable* dynamic_ref_table = nullptr;
@@ -63,12 +69,22 @@
class LoadedArsc;
class LoadedPackage {
- friend class LoadedArsc;
-
public:
+ static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
+ const LoadedIdmap* loaded_idmap, bool system,
+ bool load_as_shared_library);
+
+ ~LoadedPackage();
+
bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
- LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
- uint32_t* out_flags) const;
+ FindEntryResult* out_entry) const;
+
+ // Finds the entry with the specified type name and entry name. The names are in UTF-16 because
+ // the underlying ResStringPool API expects this. For now this is acceptable, but since
+ // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
+ // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible
+ // for patching the correct package ID to the resource ID.
+ uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
// Returns the string pool where type names are stored.
inline const ResStringPool* GetTypeStringPool() const {
@@ -98,10 +114,16 @@
return system_;
}
+ // Returns true if this package is from an overlay ApkAssets.
inline bool IsOverlay() const {
return overlay_;
}
+ // Returns true if this package is verified to be valid.
+ inline bool IsVerified() const {
+ return verified_;
+ }
+
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
// package could have been assigned a different package ID than what this LoadedPackage was
// compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
@@ -118,19 +140,14 @@
// before being inserted into the set. This may cause some equivalent locales to de-dupe.
void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const;
- // Finds the entry with the specified type name and entry name. The names are in UTF-16 because
- // the underlying ResStringPool API expects this. For now this is acceptable, but since
- // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
- // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible
- // for patching the correct package ID to the resource ID.
- uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
-
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
- static std::unique_ptr<LoadedPackage> Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap);
+ LoadedPackage();
- LoadedPackage() = default;
+ template <bool Verified>
+ bool FindEntry(const util::unique_cptr<TypeSpec>& type_spec_ptr, uint16_t entry_idx,
+ const ResTable_config& config, FindEntryResult* out_entry) const;
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
@@ -140,6 +157,7 @@
bool dynamic_ = false;
bool system_ = false;
bool overlay_ = false;
+ bool verified_ = true;
ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
@@ -163,8 +181,6 @@
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
static std::unique_ptr<const LoadedArsc> CreateEmpty();
- ~LoadedArsc();
-
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
inline const ResStringPool* GetStringPool() const {
@@ -175,8 +191,7 @@
// The parameter `out_entry` will be filled with the resulting resource entry.
// The resource entry can be a simple entry (ResTable_entry) or a complex bag
// (ResTable_entry_map).
- bool FindEntry(uint32_t resid, const ResTable_config& config, LoadedArscEntry* out_entry,
- ResTable_config* selected_config, uint32_t* out_flags) const;
+ bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const;
// Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
const LoadedPackage* GetPackageForId(uint32_t resid) const;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 8f858b6..8547955 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -546,15 +546,15 @@
*/
class StringPoolRef {
public:
- StringPoolRef();
- StringPoolRef(const ResStringPool* pool, uint32_t index);
+ StringPoolRef() = default;
+ StringPoolRef(const ResStringPool* pool, uint32_t index);
- const char* string8(size_t* outLen) const;
- const char16_t* string16(size_t* outLen) const;
+ const char* string8(size_t* outLen) const;
+ const char16_t* string16(size_t* outLen) const;
private:
- const ResStringPool* mPool;
- uint32_t mIndex;
+ const ResStringPool* mPool = nullptr;
+ uint32_t mIndex = 0u;
};
/** ********************************************************************
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 2766ce1..d65d93f 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -32,7 +32,13 @@
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
ASSERT_NE(nullptr, loaded_apk);
- EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc());
+
+ const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
+ ASSERT_NE(nullptr, loaded_package);
+ EXPECT_TRUE(loaded_package->IsVerified());
std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
ASSERT_NE(nullptr, asset);
@@ -87,6 +93,20 @@
ASSERT_NE(nullptr, loaded_overlay_apk);
}
+TEST(ApkAssetsTest, LoadUnverifiableApk) {
+ std::unique_ptr<const ApkAssets> loaded_apk =
+ ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk");
+ ASSERT_NE(nullptr, loaded_apk);
+
+ const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
+ ASSERT_NE(nullptr, loaded_package);
+
+ EXPECT_FALSE(loaded_package->IsVerified());
+}
+
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 99a07a5..739e733 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -26,10 +26,12 @@
#include "data/basic/R.h"
#include "data/libclient/R.h"
#include "data/styles/R.h"
+#include "data/unverified/R.h"
namespace app = com::android::app;
namespace basic = com::android::basic;
namespace libclient = com::android::libclient;
+namespace unverified = com::android::unverified;
namespace android {
@@ -124,6 +126,12 @@
}
BENCHMARK(BM_AssetManagerGetResourceOld);
+static void BM_AssetManagerGetResourceUnverified(benchmark::State& state) {
+ GetResourceBenchmark({GetTestDataPath() + "/unverified/unverified.apk"}, nullptr /*config*/,
+ unverified::R::integer::number1, state);
+}
+BENCHMARK(BM_AssetManagerGetResourceUnverified);
+
static void BM_AssetManagerGetLibraryResource(benchmark::State& state) {
GetResourceBenchmark(
{GetTestDataPath() + "/lib_two/lib_two.apk", GetTestDataPath() + "/lib_one/lib_one.apk",
@@ -206,6 +214,30 @@
}
BENCHMARK(BM_AssetManagerGetBagOld);
+static void BM_AssetManagerGetBagUnverified(benchmark::State& state) {
+ std::unique_ptr<const ApkAssets> apk =
+ ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk");
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ while (state.KeepRunning()) {
+ const ResolvedBag* bag = assets.GetBag(unverified::R::array::integerArray1);
+ const auto bag_end = end(bag);
+ for (auto iter = begin(bag); iter != bag_end; ++iter) {
+ uint32_t key = iter->key;
+ Res_value value = iter->value;
+ benchmark::DoNotOptimize(key);
+ benchmark::DoNotOptimize(value);
+ }
+ }
+}
+BENCHMARK(BM_AssetManagerGetBagUnverified);
+
static void BM_AssetManagerGetResourceLocales(benchmark::State& state) {
std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
if (apk == nullptr) {
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index fcae53b..ab1a22e 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -28,6 +28,7 @@
#include "data/libclient/R.h"
#include "data/styles/R.h"
#include "data/system/R.h"
+#include "data/unverified/R.h"
namespace app = com::android::app;
namespace appaslib = com::android::appaslib::app;
@@ -35,6 +36,7 @@
namespace lib_one = com::android::lib_one;
namespace lib_two = com::android::lib_two;
namespace libclient = com::android::libclient;
+namespace unverified = com::android::unverified;
namespace android {
@@ -431,4 +433,30 @@
TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
+TEST_F(AssetManager2Test, OperateOnUnverifiedApkAssets) {
+ std::unique_ptr<const ApkAssets> unverified_assets =
+ ApkAssets::Load(GetTestDataPath() + "/unverified/unverified.apk");
+ ASSERT_NE(nullptr, unverified_assets);
+
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({unverified_assets.get()});
+
+ Res_value value;
+ ResTable_config config;
+ uint32_t flags;
+
+ EXPECT_EQ(kInvalidCookie,
+ assetmanager.GetResource(unverified::R::string::test1, false /*may_be_bag*/, 0u, &value,
+ &config, &flags));
+ EXPECT_EQ(kInvalidCookie,
+ assetmanager.GetResource(unverified::R::string::test2, false /*may_be_bag*/, 0u, &value,
+ &config, &flags));
+ EXPECT_NE(kInvalidCookie,
+ assetmanager.GetResource(unverified::R::integer::number1, false /*may_be_bag*/, 0u,
+ &value, &config, &flags));
+
+ EXPECT_EQ(nullptr, assetmanager.GetBag(unverified::R::style::Theme1));
+ EXPECT_NE(nullptr, assetmanager.GetBag(unverified::R::array::integerArray1));
+}
+
} // namespace android
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 2b72d14..954a54d 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -44,12 +44,9 @@
memset(&config, 0, sizeof(config));
config.sdkVersion = 24;
- LoadedArscEntry entry;
- ResTable_config selected_config;
- uint32_t flags;
+ FindEntryResult entry;
- ASSERT_TRUE(
- loaded_arsc->FindEntry(app::R::string::string_one, config, &entry, &selected_config, &flags));
+ ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry));
ASSERT_NE(nullptr, entry.entry);
}
@@ -66,12 +63,8 @@
desired_config.language[0] = 'd';
desired_config.language[1] = 'e';
- LoadedArscEntry entry;
- ResTable_config selected_config;
- uint32_t flags;
-
- ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry,
- &selected_config, &flags));
+ FindEntryResult entry;
+ ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry));
ASSERT_NE(nullptr, entry.entry);
}
@@ -150,23 +143,15 @@
ResTable_config desired_config;
memset(&desired_config, 0, sizeof(desired_config));
- LoadedArscEntry entry;
- ResTable_config selected_config;
- uint32_t flags;
-
- ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry,
- &selected_config, &flags));
+ FindEntryResult entry;
+ ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry));
size_t len;
const char16_t* type_name16 = entry.type_string_ref.string16(&len);
ASSERT_NE(nullptr, type_name16);
ASSERT_NE(0u, len);
- size_t utf8_len = utf16_to_utf8_length(type_name16, len);
- std::string type_name;
- type_name.resize(utf8_len);
- utf16_to_utf8(type_name16, len, &*type_name.begin(), utf8_len + 1);
-
+ std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len));
EXPECT_EQ(std::string("string"), type_name);
}
@@ -210,12 +195,8 @@
ResTable_config desired_config;
memset(&desired_config, 0, sizeof(desired_config));
- LoadedArscEntry entry;
- ResTable_config selected_config;
- uint32_t flags;
-
- ASSERT_TRUE(
- loaded_arsc->FindEntry(0x08030001u, desired_config, &entry, &selected_config, &flags));
+ FindEntryResult entry;
+ ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry));
}
// structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/data/unverified/R.h b/libs/androidfw/tests/data/unverified/R.h
new file mode 100644
index 0000000..b734b49
--- /dev/null
+++ b/libs/androidfw/tests/data/unverified/R.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef TESTS_DATA_UNVERIFIED_R_H_
+#define TESTS_DATA_UNVERIFIED_R_H_
+
+#include <cstdint>
+
+#include "tests/data/basic/R.h"
+
+namespace com {
+namespace android {
+
+namespace unverified = basic;
+
+} // namespace android
+} // namespace com
+
+#endif /* TESTS_DATA_UNVERIFIED_R_H_ */
diff --git a/libs/androidfw/tests/data/unverified/unverified.apk b/libs/androidfw/tests/data/unverified/unverified.apk
new file mode 100644
index 0000000..234b390
--- /dev/null
+++ b/libs/androidfw/tests/data/unverified/unverified.apk
Binary files differ