• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define ATRACE_TAG ATRACE_TAG_RESOURCES
18 
19 #include "androidfw/LoadedArsc.h"
20 
21 #include <algorithm>
22 #include <cstddef>
23 #include <limits>
24 
25 #include "android-base/logging.h"
26 #include "android-base/stringprintf.h"
27 #include "utils/ByteOrder.h"
28 #include "utils/Trace.h"
29 
30 #ifdef _WIN32
31 #ifdef ERROR
32 #undef ERROR
33 #endif
34 #endif
35 
36 #include "androidfw/Chunk.h"
37 #include "androidfw/ResourceUtils.h"
38 #include "androidfw/Util.h"
39 
40 using android::base::StringPrintf;
41 
42 namespace android {
43 
44 constexpr const static int kFrameworkPackageId = 0x01;
45 constexpr const static int kAppPackageId = 0x7f;
46 
47 namespace {
48 
49 // Builder that helps accumulate Type structs and then create a single
50 // contiguous block of memory to store both the TypeSpec struct and
51 // the Type structs.
52 struct TypeSpecBuilder {
TypeSpecBuilderandroid::__anone41ffcce0111::TypeSpecBuilder53   explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) {}
54 
AddTypeandroid::__anone41ffcce0111::TypeSpecBuilder55   void AddType(incfs::verified_map_ptr<ResTable_type> type) {
56     TypeSpec::TypeEntry& entry = type_entries.emplace_back();
57     entry.config.copyFromDtoH(type->config);
58     entry.type = type;
59   }
60 
Buildandroid::__anone41ffcce0111::TypeSpecBuilder61   TypeSpec Build() {
62     return {header_, std::move(type_entries)};
63   }
64 
65  private:
66   DISALLOW_COPY_AND_ASSIGN(TypeSpecBuilder);
67 
68   incfs::verified_map_ptr<ResTable_typeSpec> header_;
69   std::vector<TypeSpec::TypeEntry> type_entries;
70 };
71 
72 }  // namespace
73 
74 // Precondition: The header passed in has already been verified, so reading any fields and trusting
75 // the ResChunk_header is safe.
VerifyResTableType(incfs::map_ptr<ResTable_type> header)76 static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
77   if (header->id == 0) {
78     LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0.";
79     return false;
80   }
81 
82   const size_t entry_count = dtohl(header->entryCount);
83   if (entry_count > std::numeric_limits<uint16_t>::max()) {
84     LOG(ERROR) << "RES_TABLE_TYPE_TYPE has too many entries (" << entry_count << ").";
85     return false;
86   }
87 
88   // Make sure that there is enough room for the entry offsets.
89   const size_t offsets_offset = dtohs(header->header.headerSize);
90   const size_t entries_offset = dtohl(header->entriesStart);
91   const size_t offsets_length = sizeof(uint32_t) * entry_count;
92 
93   if (offsets_offset > entries_offset || entries_offset - offsets_offset < offsets_length) {
94     LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data.";
95     return false;
96   }
97 
98   if (entries_offset > dtohl(header->header.size)) {
99     LOG(ERROR) << "RES_TABLE_TYPE_TYPE entry offsets extend beyond chunk.";
100     return false;
101   }
102 
103   if (entries_offset & 0x03U) {
104     LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address.";
105     return false;
106   }
107   return true;
108 }
109 
VerifyResTableEntry(incfs::verified_map_ptr<ResTable_type> type,uint32_t entry_offset)110 static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry(
111     incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) {
112   // Check that the offset is aligned.
113   if (UNLIKELY(entry_offset & 0x03U)) {
114     LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
115     return base::unexpected(std::nullopt);
116   }
117 
118   // Check that the offset doesn't overflow.
119   if (UNLIKELY(entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart))) {
120     // Overflow in offset.
121     LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
122     return base::unexpected(std::nullopt);
123   }
124 
125   const size_t chunk_size = dtohl(type->header.size);
126 
127   entry_offset += dtohl(type->entriesStart);
128   if (UNLIKELY(entry_offset > chunk_size - sizeof(ResTable_entry))) {
129     LOG(ERROR) << "Entry at offset " << entry_offset
130                << " is too large. No room for ResTable_entry.";
131     return base::unexpected(std::nullopt);
132   }
133 
134   auto entry = type.offset(entry_offset).convert<ResTable_entry>();
135   if (UNLIKELY(!entry)) {
136     return base::unexpected(IOError::PAGES_MISSING);
137   }
138 
139   const size_t entry_size = dtohs(entry->size);
140   if (UNLIKELY(entry_size < sizeof(entry.value()))) {
141     LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
142                << " is too small.";
143     return base::unexpected(std::nullopt);
144   }
145 
146   if (UNLIKELY(entry_size > chunk_size || entry_offset > chunk_size - entry_size)) {
147     LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
148                << " is too large.";
149     return base::unexpected(std::nullopt);
150   }
151 
152   if (entry_size < sizeof(ResTable_map_entry)) {
153     // There needs to be room for one Res_value struct.
154     if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) {
155       LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
156                  << " for type " << (int)type->id << ".";
157       return base::unexpected(std::nullopt);
158     }
159 
160     auto value = entry.offset(entry_size).convert<Res_value>();
161     if (UNLIKELY(!value)) {
162        return base::unexpected(IOError::PAGES_MISSING);
163     }
164 
165     const size_t value_size = dtohs(value->size);
166     if (UNLIKELY(value_size < sizeof(Res_value))) {
167       LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small.";
168       return base::unexpected(std::nullopt);
169     }
170 
171     if (UNLIKELY(value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size)) {
172       LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
173                  << " is too large.";
174       return base::unexpected(std::nullopt);
175     }
176   } else {
177     auto map = entry.convert<ResTable_map_entry>();
178     if (UNLIKELY(!map)) {
179       return base::unexpected(IOError::PAGES_MISSING);
180     }
181 
182     const size_t map_entry_count = dtohl(map->count);
183     size_t map_entries_start = entry_offset + entry_size;
184     if (UNLIKELY(map_entries_start & 0x03U)) {
185       LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset.";
186       return base::unexpected(std::nullopt);
187     }
188 
189     // Each entry is sizeof(ResTable_map) big.
190     if (UNLIKELY(map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map)))) {
191       LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
192       return base::unexpected(std::nullopt);
193     }
194   }
195   return {};
196 }
197 
iterator(const LoadedPackage * lp,size_t ti,size_t ei)198 LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei)
199     : loadedPackage_(lp),
200       typeIndex_(ti),
201       entryIndex_(ei),
202       typeIndexEnd_(lp->resource_ids_.size() + 1) {
203   while (typeIndex_ < typeIndexEnd_ && loadedPackage_->resource_ids_[typeIndex_] == 0) {
204     typeIndex_++;
205   }
206 }
207 
operator ++()208 LoadedPackage::iterator& LoadedPackage::iterator::operator++() {
209   while (typeIndex_ < typeIndexEnd_) {
210     if (entryIndex_ + 1 < loadedPackage_->resource_ids_[typeIndex_]) {
211       entryIndex_++;
212       break;
213     }
214     entryIndex_ = 0;
215     typeIndex_++;
216     if (typeIndex_ < typeIndexEnd_ && loadedPackage_->resource_ids_[typeIndex_] != 0) {
217       break;
218     }
219   }
220   return *this;
221 }
222 
operator *() const223 uint32_t LoadedPackage::iterator::operator*() const {
224   if (typeIndex_ >= typeIndexEnd_) {
225     return 0;
226   }
227   return make_resid(loadedPackage_->package_id_, typeIndex_ + loadedPackage_->type_id_offset_,
228           entryIndex_);
229 }
230 
GetEntry(incfs::verified_map_ptr<ResTable_type> type_chunk,uint16_t entry_index)231 base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry(
232     incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
233   base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index);
234   if (UNLIKELY(!entry_offset.has_value())) {
235     return base::unexpected(entry_offset.error());
236   }
237   return GetEntryFromOffset(type_chunk, entry_offset.value());
238 }
239 
GetEntryOffset(incfs::verified_map_ptr<ResTable_type> type_chunk,uint16_t entry_index)240 base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset(
241     incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) {
242   // The configuration matches and is better than the previous selection.
243   // Find the entry value if it exists for this configuration.
244   const size_t entry_count = dtohl(type_chunk->entryCount);
245   const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
246 
247   // Check if there is the desired entry in this type.
248   if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
249     // This is encoded as a sparse map, so perform a binary search.
250     bool error = false;
251     auto sparse_indices = type_chunk.offset(offsets_offset)
252                                     .convert<ResTable_sparseTypeEntry>().iterator();
253     auto sparse_indices_end = sparse_indices + entry_count;
254     auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
255                                    [&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry,
256                                             uint16_t entry_idx) {
257       if (UNLIKELY(!entry)) {
258         return error = true;
259       }
260       return dtohs(entry->idx) < entry_idx;
261     });
262 
263     if (result == sparse_indices_end) {
264       // No entry found.
265       return base::unexpected(std::nullopt);
266     }
267 
268     const incfs::verified_map_ptr<ResTable_sparseTypeEntry> entry = (*result).verified();
269     if (dtohs(entry->idx) != entry_index) {
270       if (error) {
271         return base::unexpected(IOError::PAGES_MISSING);
272       }
273       return base::unexpected(std::nullopt);
274     }
275 
276     // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
277     // the real offset divided by 4.
278     return uint32_t{dtohs(entry->offset)} * 4u;
279   }
280 
281   // This type is encoded as a dense array.
282   if (entry_index >= entry_count) {
283     // This entry cannot be here.
284     return base::unexpected(std::nullopt);
285   }
286 
287   const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index;
288   if (UNLIKELY(!entry_offset_ptr)) {
289     return base::unexpected(IOError::PAGES_MISSING);
290   }
291 
292   const uint32_t value = dtohl(entry_offset_ptr.value());
293   if (value == ResTable_type::NO_ENTRY) {
294     return base::unexpected(std::nullopt);
295   }
296 
297   return value;
298 }
299 
GetEntryFromOffset(incfs::verified_map_ptr<ResTable_type> type_chunk,uint32_t offset)300 base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset(
301     incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) {
302   auto valid = VerifyResTableEntry(type_chunk, offset);
303   if (UNLIKELY(!valid.has_value())) {
304     return base::unexpected(valid.error());
305   }
306   return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>();
307 }
308 
CollectConfigurations(bool exclude_mipmap,std::set<ResTable_config> * out_configs) const309 base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations(
310     bool exclude_mipmap, std::set<ResTable_config>* out_configs) const {\
311   for (const auto& type_spec : type_specs_) {
312     if (exclude_mipmap) {
313       const int type_idx = type_spec.first - 1;
314       const auto type_name16 = type_string_pool_.stringAt(type_idx);
315       if (UNLIKELY(IsIOError(type_name16))) {
316         return base::unexpected(GetIOError(type_name16.error()));
317       }
318       if (type_name16.has_value()) {
319         if (strncmp16(type_name16->data(), u"mipmap", type_name16->size()) == 0) {
320           // This is a mipmap type, skip collection.
321           continue;
322         }
323       }
324 
325       const auto type_name = type_string_pool_.string8At(type_idx);
326       if (UNLIKELY(IsIOError(type_name))) {
327         return base::unexpected(GetIOError(type_name.error()));
328       }
329       if (type_name.has_value()) {
330         if (strncmp(type_name->data(), "mipmap", type_name->size()) == 0) {
331           // This is a mipmap type, skip collection.
332           continue;
333         }
334       }
335     }
336 
337     for (const auto& type_entry : type_spec.second.type_entries) {
338       out_configs->insert(type_entry.config);
339     }
340   }
341   return {};
342 }
343 
CollectLocales(bool canonicalize,std::set<std::string> * out_locales) const344 void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const {
345   char temp_locale[RESTABLE_MAX_LOCALE_LEN];
346   for (const auto& type_spec : type_specs_) {
347     for (const auto& type_entry : type_spec.second.type_entries) {
348       if (type_entry.config.locale != 0) {
349         type_entry.config.getBcp47Locale(temp_locale, canonicalize);
350         std::string locale(temp_locale);
351         out_locales->insert(std::move(locale));
352       }
353     }
354   }
355 }
356 
FindEntryByName(const std::u16string & type_name,const std::u16string & entry_name) const357 base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName(
358     const std::u16string& type_name, const std::u16string& entry_name) const {
359   const base::expected<size_t, NullOrIOError> type_idx = type_string_pool_.indexOfString(
360       type_name.data(), type_name.size());
361   if (!type_idx.has_value()) {
362     return base::unexpected(type_idx.error());
363   }
364 
365   const base::expected<size_t, NullOrIOError> key_idx = key_string_pool_.indexOfString(
366       entry_name.data(), entry_name.size());
367   if (!key_idx.has_value()) {
368     return base::unexpected(key_idx.error());
369   }
370 
371   const TypeSpec* type_spec = GetTypeSpecByTypeIndex(*type_idx);
372   if (type_spec == nullptr) {
373     return base::unexpected(std::nullopt);
374   }
375 
376   for (const auto& type_entry : type_spec->type_entries) {
377     const incfs::verified_map_ptr<ResTable_type>& type = type_entry.type;
378 
379     size_t entry_count = dtohl(type->entryCount);
380     for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
381       auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() +
382           entry_idx;
383       if (!entry_offset_ptr) {
384         return base::unexpected(IOError::PAGES_MISSING);
385       }
386 
387       uint32_t offset;
388       uint16_t res_idx;
389       if (type->flags & ResTable_type::FLAG_SPARSE) {
390         auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
391         offset = dtohs(sparse_entry->offset) * 4u;
392         res_idx  = dtohs(sparse_entry->idx);
393       } else {
394         offset = dtohl(entry_offset_ptr.value());
395         res_idx = entry_idx;
396       }
397       if (offset != ResTable_type::NO_ENTRY) {
398         auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
399         if (!entry) {
400           return base::unexpected(IOError::PAGES_MISSING);
401         }
402 
403         if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
404           // The package ID will be overridden by the caller (due to runtime assignment of package
405           // IDs for shared libraries).
406           return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);
407         }
408       }
409     }
410   }
411   return base::unexpected(std::nullopt);
412 }
413 
GetPackageById(uint8_t package_id) const414 const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
415   for (const auto& loaded_package : packages_) {
416     if (loaded_package->GetPackageId() == package_id) {
417       return loaded_package.get();
418     }
419   }
420   return nullptr;
421 }
422 
Load(const Chunk & chunk,package_property_t property_flags)423 std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
424                                                          package_property_t property_flags) {
425   ATRACE_NAME("LoadedPackage::Load");
426   std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
427 
428   // typeIdOffset was added at some point, but we still must recognize apps built before this
429   // was added.
430   constexpr size_t kMinPackageSize =
431       sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
432   const incfs::map_ptr<ResTable_package> header = chunk.header<ResTable_package, kMinPackageSize>();
433   if (!header) {
434     LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small.";
435     return {};
436   }
437 
438   if ((property_flags & PROPERTY_SYSTEM) != 0) {
439     loaded_package->property_flags_ |= PROPERTY_SYSTEM;
440   }
441 
442   if ((property_flags & PROPERTY_LOADER) != 0) {
443     loaded_package->property_flags_ |= PROPERTY_LOADER;
444   }
445 
446   if ((property_flags & PROPERTY_OVERLAY) != 0) {
447     // Overlay resources must have an exclusive resource id space for referencing internal
448     // resources.
449     loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC;
450   }
451 
452   loaded_package->package_id_ = dtohl(header->id);
453   if (loaded_package->package_id_ == 0 ||
454       (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) {
455     loaded_package->property_flags_ |= PROPERTY_DYNAMIC;
456   }
457 
458   if (header->header.headerSize >= sizeof(ResTable_package)) {
459     uint32_t type_id_offset = dtohl(header->typeIdOffset);
460     if (type_id_offset > std::numeric_limits<uint8_t>::max()) {
461       LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE type ID offset too large.";
462       return {};
463     }
464     loaded_package->type_id_offset_ = static_cast<int>(type_id_offset);
465   }
466 
467   util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
468                                   &loaded_package->package_name_);
469 
470   // A map of TypeSpec builders, each associated with an type index.
471   // We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
472   // contiguous block of memory that holds all the Types together with the TypeSpec.
473   std::unordered_map<int, std::unique_ptr<TypeSpecBuilder>> type_builder_map;
474 
475   ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
476   while (iter.HasNext()) {
477     const Chunk child_chunk = iter.Next();
478     switch (child_chunk.type()) {
479       case RES_STRING_POOL_TYPE: {
480         const auto pool_address = child_chunk.header<ResChunk_header>();
481         if (!pool_address) {
482           LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation.";
483           return {};
484         }
485 
486         if (pool_address == header.offset(dtohl(header->typeStrings)).convert<ResChunk_header>()) {
487           // This string pool is the type string pool.
488           status_t err = loaded_package->type_string_pool_.setTo(
489               child_chunk.header<ResStringPool_header>(), child_chunk.size());
490           if (err != NO_ERROR) {
491             LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt.";
492             return {};
493           }
494         } else if (pool_address == header.offset(dtohl(header->keyStrings))
495                                          .convert<ResChunk_header>()) {
496           // This string pool is the key string pool.
497           status_t err = loaded_package->key_string_pool_.setTo(
498               child_chunk.header<ResStringPool_header>(), child_chunk.size());
499           if (err != NO_ERROR) {
500             LOG(ERROR) << "RES_STRING_POOL_TYPE for keys corrupt.";
501             return {};
502           }
503         } else {
504           LOG(WARNING) << "Too many RES_STRING_POOL_TYPEs found in RES_TABLE_PACKAGE_TYPE.";
505         }
506       } break;
507 
508       case RES_TABLE_TYPE_SPEC_TYPE: {
509         const auto type_spec = child_chunk.header<ResTable_typeSpec>();
510         if (!type_spec) {
511           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
512           return {};
513         }
514 
515         if (type_spec->id == 0) {
516           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
517           return {};
518         }
519 
520         if (loaded_package->type_id_offset_ + static_cast<int>(type_spec->id) >
521             std::numeric_limits<uint8_t>::max()) {
522           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has out of range ID.";
523           return {};
524         }
525 
526         // The data portion of this chunk contains entry_count 32bit entries,
527         // each one representing a set of flags.
528         // Here we only validate that the chunk is well formed.
529         const size_t entry_count = dtohl(type_spec->entryCount);
530 
531         // There can only be 2^16 entries in a type, because that is the ID
532         // space for entries (EEEE) in the resource ID 0xPPTTEEEE.
533         if (entry_count > std::numeric_limits<uint16_t>::max()) {
534           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has too many entries (" << entry_count << ").";
535           return {};
536         }
537 
538         if (entry_count * sizeof(uint32_t) > chunk.data_size()) {
539           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small to hold entries.";
540           return {};
541         }
542 
543         std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type_spec->id];
544         if (builder_ptr == nullptr) {
545           builder_ptr = util::make_unique<TypeSpecBuilder>(type_spec.verified());
546           loaded_package->resource_ids_.set(type_spec->id, entry_count);
547         } else {
548           LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
549                                        type_spec->id);
550         }
551       } break;
552 
553       case RES_TABLE_TYPE_TYPE: {
554         const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
555         if (!type) {
556           LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small.";
557           return {};
558         }
559 
560         if (!VerifyResTableType(type)) {
561           return {};
562         }
563 
564         // Type chunks must be preceded by their TypeSpec chunks.
565         std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type->id];
566         if (builder_ptr != nullptr) {
567           builder_ptr->AddType(type.verified());
568         } else {
569           LOG(ERROR) << StringPrintf(
570               "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
571               type->id);
572           return {};
573         }
574       } break;
575 
576       case RES_TABLE_LIBRARY_TYPE: {
577         const auto lib = child_chunk.header<ResTable_lib_header>();
578         if (!lib) {
579           LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small.";
580           return {};
581         }
582 
583         if (child_chunk.data_size() / sizeof(ResTable_lib_entry) < dtohl(lib->count)) {
584           LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small to hold entries.";
585           return {};
586         }
587 
588         loaded_package->dynamic_package_map_.reserve(dtohl(lib->count));
589 
590         const auto entry_begin = child_chunk.data_ptr().convert<ResTable_lib_entry>();
591         const auto entry_end = entry_begin + dtohl(lib->count);
592         for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
593           if (!entry_iter) {
594             return {};
595           }
596 
597           std::string package_name;
598           util::ReadUtf16StringFromDevice(entry_iter->packageName,
599                                           arraysize(entry_iter->packageName), &package_name);
600 
601           if (dtohl(entry_iter->packageId) >= std::numeric_limits<uint8_t>::max()) {
602             LOG(ERROR) << StringPrintf(
603                 "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.",
604                 dtohl(entry_iter->packageId), package_name.c_str());
605             return {};
606           }
607 
608           loaded_package->dynamic_package_map_.emplace_back(std::move(package_name),
609                                                             dtohl(entry_iter->packageId));
610         }
611       } break;
612 
613       case RES_TABLE_OVERLAYABLE_TYPE: {
614         const auto overlayable = child_chunk.header<ResTable_overlayable_header>();
615         if (!overlayable) {
616           LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
617           return {};
618         }
619 
620         std::string name;
621         util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
622         std::string actor;
623         util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
624 
625         if (loaded_package->overlayable_map_.find(name) !=
626             loaded_package->overlayable_map_.end()) {
627           LOG(ERROR) << "Multiple <overlayable> blocks with the same name '" << name << "'.";
628           return {};
629         }
630         loaded_package->overlayable_map_.emplace(name, actor);
631 
632         // Iterate over the overlayable policy chunks contained within the overlayable chunk data
633         ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
634         while (overlayable_iter.HasNext()) {
635           const Chunk overlayable_child_chunk = overlayable_iter.Next();
636 
637           switch (overlayable_child_chunk.type()) {
638             case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
639               const auto policy_header =
640                   overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
641               if (!policy_header) {
642                 LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
643                 return {};
644               }
645 
646               if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
647                   < dtohl(policy_header->entry_count)) {
648                 LOG(ERROR) <<  "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
649                 return {};
650               }
651 
652               // Retrieve all the resource ids belonging to this policy chunk
653               const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>();
654               const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
655               std::unordered_set<uint32_t> ids;
656               ids.reserve(ids_end - ids_begin);
657               for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
658                 if (!id_iter) {
659                   LOG(ERROR) << "NULL ResTable_ref record??";
660                   return {};
661                 }
662                 ids.insert(dtohl(id_iter->ident));
663               }
664 
665               // Add the pairing of overlayable properties and resource ids to the package
666               OverlayableInfo overlayable_info {
667                 .name = name,
668                 .actor = actor,
669                 .policy_flags = policy_header->policy_flags
670               };
671               loaded_package->overlayable_infos_.emplace_back(std::move(overlayable_info), std::move(ids));
672               loaded_package->defines_overlayable_ = true;
673               break;
674             }
675 
676             default:
677               LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
678               break;
679           }
680         }
681 
682         if (overlayable_iter.HadError()) {
683           LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_TYPE: %s",
684                                      overlayable_iter.GetLastError().c_str());
685           if (overlayable_iter.HadFatalError()) {
686             return {};
687           }
688         }
689       } break;
690 
691       case RES_TABLE_STAGED_ALIAS_TYPE: {
692         if (loaded_package->package_id_ != kFrameworkPackageId) {
693           LOG(WARNING) << "Alias chunk ignored for non-framework package '"
694                        << loaded_package->package_name_ << "'";
695           break;
696         }
697 
698         const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
699         if (!lib_alias) {
700           LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small.";
701           return {};
702         }
703         if ((child_chunk.data_size() / sizeof(ResTable_staged_alias_entry))
704             < dtohl(lib_alias->count)) {
705           LOG(ERROR) << "RES_TABLE_STAGED_ALIAS_TYPE is too small to hold entries.";
706           return {};
707         }
708         const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
709         const auto entry_end = entry_begin + dtohl(lib_alias->count);
710         std::unordered_set<uint32_t> finalized_ids;
711         finalized_ids.reserve(entry_end - entry_begin);
712         for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
713           if (!entry_iter) {
714             LOG(ERROR) << "NULL ResTable_staged_alias_entry record??";
715             return {};
716           }
717           auto finalized_id = dtohl(entry_iter->finalizedResId);
718           if (!finalized_ids.insert(finalized_id).second) {
719             LOG(ERROR) << StringPrintf("Repeated finalized resource id '%08x' in staged aliases.",
720                                        finalized_id);
721             return {};
722           }
723 
724           auto staged_id = dtohl(entry_iter->stagedResId);
725           auto [_, success] = loaded_package->alias_id_map_.emplace(staged_id, finalized_id);
726           if (!success) {
727             LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
728                                        staged_id);
729             return {};
730           }
731         }
732       } break;
733 
734       default:
735         LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
736         break;
737     }
738   }
739 
740   if (iter.HadError()) {
741     LOG(ERROR) << iter.GetLastError();
742     if (iter.HadFatalError()) {
743       return {};
744     }
745   }
746 
747   // Flatten and construct the TypeSpecs.
748   for (auto& entry : type_builder_map) {
749     TypeSpec type_spec = entry.second->Build();
750     uint8_t type_id = static_cast<uint8_t>(entry.first);
751     loaded_package->type_specs_[type_id] = std::move(type_spec);
752   }
753 
754   return std::move(loaded_package);
755 }
756 
LoadTable(const Chunk & chunk,const LoadedIdmap * loaded_idmap,package_property_t property_flags)757 bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
758                            package_property_t property_flags) {
759   incfs::map_ptr<ResTable_header> header = chunk.header<ResTable_header>();
760   if (!header) {
761     LOG(ERROR) << "RES_TABLE_TYPE too small.";
762     return false;
763   }
764 
765   if (loaded_idmap != nullptr) {
766     global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
767   }
768 
769   const size_t package_count = dtohl(header->packageCount);
770   size_t packages_seen = 0;
771 
772   packages_.reserve(package_count);
773 
774   ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
775   while (iter.HasNext()) {
776     const Chunk child_chunk = iter.Next();
777     switch (child_chunk.type()) {
778       case RES_STRING_POOL_TYPE:
779         // Only use the first string pool. Ignore others.
780         if (global_string_pool_->getError() == NO_INIT) {
781           status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(),
782                                                     child_chunk.size());
783           if (err != NO_ERROR) {
784             LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt.";
785             return false;
786           }
787         } else {
788           LOG(WARNING) << "Multiple RES_STRING_POOL_TYPEs found in RES_TABLE_TYPE.";
789         }
790         break;
791 
792       case RES_TABLE_PACKAGE_TYPE: {
793         if (packages_seen + 1 > package_count) {
794           LOG(ERROR) << "More package chunks were found than the " << package_count
795                      << " declared in the header.";
796           return false;
797         }
798         packages_seen++;
799 
800         std::unique_ptr<const LoadedPackage> loaded_package =
801             LoadedPackage::Load(child_chunk, property_flags);
802         if (!loaded_package) {
803           return false;
804         }
805         packages_.push_back(std::move(loaded_package));
806       } break;
807 
808       default:
809         LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
810         break;
811     }
812   }
813 
814   if (iter.HadError()) {
815     LOG(ERROR) << iter.GetLastError();
816     if (iter.HadFatalError()) {
817       return false;
818     }
819   }
820   return true;
821 }
822 
Load(incfs::map_ptr<void> data,const size_t length,const LoadedIdmap * loaded_idmap,const package_property_t property_flags)823 std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
824                                              const size_t length,
825                                              const LoadedIdmap* loaded_idmap,
826                                              const package_property_t property_flags) {
827   ATRACE_NAME("LoadedArsc::Load");
828 
829   // Not using make_unique because the constructor is private.
830   std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
831 
832   ChunkIterator iter(data, length);
833   while (iter.HasNext()) {
834     const Chunk chunk = iter.Next();
835     switch (chunk.type()) {
836       case RES_TABLE_TYPE:
837         if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) {
838           return {};
839         }
840         break;
841 
842       default:
843         LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
844         break;
845     }
846   }
847 
848   if (iter.HadError()) {
849     LOG(ERROR) << iter.GetLastError();
850     if (iter.HadFatalError()) {
851       return {};
852     }
853   }
854 
855   return loaded_arsc;
856 }
857 
CreateEmpty()858 std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
859   return std::unique_ptr<LoadedArsc>(new LoadedArsc());
860 }
861 
862 }  // namespace android
863