• 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::__anonc2efba690111::TypeSpecBuilder53   explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) {}
54 
AddTypeandroid::__anonc2efba690111::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::__anonc2efba690111::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       auto offset = dtohl(entry_offset_ptr.value());
388       if (offset != ResTable_type::NO_ENTRY) {
389         auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
390         if (!entry) {
391           return base::unexpected(IOError::PAGES_MISSING);
392         }
393 
394         if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
395           // The package ID will be overridden by the caller (due to runtime assignment of package
396           // IDs for shared libraries).
397           return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
398         }
399       }
400     }
401   }
402   return base::unexpected(std::nullopt);
403 }
404 
GetPackageById(uint8_t package_id) const405 const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
406   for (const auto& loaded_package : packages_) {
407     if (loaded_package->GetPackageId() == package_id) {
408       return loaded_package.get();
409     }
410   }
411   return nullptr;
412 }
413 
Load(const Chunk & chunk,package_property_t property_flags)414 std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
415                                                          package_property_t property_flags) {
416   ATRACE_NAME("LoadedPackage::Load");
417   std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
418 
419   // typeIdOffset was added at some point, but we still must recognize apps built before this
420   // was added.
421   constexpr size_t kMinPackageSize =
422       sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
423   const incfs::map_ptr<ResTable_package> header = chunk.header<ResTable_package, kMinPackageSize>();
424   if (!header) {
425     LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small.";
426     return {};
427   }
428 
429   if ((property_flags & PROPERTY_SYSTEM) != 0) {
430     loaded_package->property_flags_ |= PROPERTY_SYSTEM;
431   }
432 
433   if ((property_flags & PROPERTY_LOADER) != 0) {
434     loaded_package->property_flags_ |= PROPERTY_LOADER;
435   }
436 
437   if ((property_flags & PROPERTY_OVERLAY) != 0) {
438     // Overlay resources must have an exclusive resource id space for referencing internal
439     // resources.
440     loaded_package->property_flags_ |= PROPERTY_OVERLAY | PROPERTY_DYNAMIC;
441   }
442 
443   loaded_package->package_id_ = dtohl(header->id);
444   if (loaded_package->package_id_ == 0 ||
445       (loaded_package->package_id_ == kAppPackageId && (property_flags & PROPERTY_DYNAMIC) != 0)) {
446     loaded_package->property_flags_ |= PROPERTY_DYNAMIC;
447   }
448 
449   if (header->header.headerSize >= sizeof(ResTable_package)) {
450     uint32_t type_id_offset = dtohl(header->typeIdOffset);
451     if (type_id_offset > std::numeric_limits<uint8_t>::max()) {
452       LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE type ID offset too large.";
453       return {};
454     }
455     loaded_package->type_id_offset_ = static_cast<int>(type_id_offset);
456   }
457 
458   util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
459                                   &loaded_package->package_name_);
460 
461   // A map of TypeSpec builders, each associated with an type index.
462   // We use these to accumulate the set of Types available for a TypeSpec, and later build a single,
463   // contiguous block of memory that holds all the Types together with the TypeSpec.
464   std::unordered_map<int, std::unique_ptr<TypeSpecBuilder>> type_builder_map;
465 
466   ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
467   while (iter.HasNext()) {
468     const Chunk child_chunk = iter.Next();
469     switch (child_chunk.type()) {
470       case RES_STRING_POOL_TYPE: {
471         const auto pool_address = child_chunk.header<ResChunk_header>();
472         if (!pool_address) {
473           LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation.";
474           return {};
475         }
476 
477         if (pool_address == header.offset(dtohl(header->typeStrings)).convert<ResChunk_header>()) {
478           // This string pool is the type string pool.
479           status_t err = loaded_package->type_string_pool_.setTo(
480               child_chunk.header<ResStringPool_header>(), child_chunk.size());
481           if (err != NO_ERROR) {
482             LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt.";
483             return {};
484           }
485         } else if (pool_address == header.offset(dtohl(header->keyStrings))
486                                          .convert<ResChunk_header>()) {
487           // This string pool is the key string pool.
488           status_t err = loaded_package->key_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 keys corrupt.";
492             return {};
493           }
494         } else {
495           LOG(WARNING) << "Too many RES_STRING_POOL_TYPEs found in RES_TABLE_PACKAGE_TYPE.";
496         }
497       } break;
498 
499       case RES_TABLE_TYPE_SPEC_TYPE: {
500         const auto type_spec = child_chunk.header<ResTable_typeSpec>();
501         if (!type_spec) {
502           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small.";
503           return {};
504         }
505 
506         if (type_spec->id == 0) {
507           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has invalid ID 0.";
508           return {};
509         }
510 
511         if (loaded_package->type_id_offset_ + static_cast<int>(type_spec->id) >
512             std::numeric_limits<uint8_t>::max()) {
513           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has out of range ID.";
514           return {};
515         }
516 
517         // The data portion of this chunk contains entry_count 32bit entries,
518         // each one representing a set of flags.
519         // Here we only validate that the chunk is well formed.
520         const size_t entry_count = dtohl(type_spec->entryCount);
521 
522         // There can only be 2^16 entries in a type, because that is the ID
523         // space for entries (EEEE) in the resource ID 0xPPTTEEEE.
524         if (entry_count > std::numeric_limits<uint16_t>::max()) {
525           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE has too many entries (" << entry_count << ").";
526           return {};
527         }
528 
529         if (entry_count * sizeof(uint32_t) > chunk.data_size()) {
530           LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small to hold entries.";
531           return {};
532         }
533 
534         std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type_spec->id];
535         if (builder_ptr == nullptr) {
536           builder_ptr = util::make_unique<TypeSpecBuilder>(type_spec.verified());
537           loaded_package->resource_ids_.set(type_spec->id, entry_count);
538         } else {
539           LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
540                                        type_spec->id);
541         }
542       } break;
543 
544       case RES_TABLE_TYPE_TYPE: {
545         const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>();
546         if (!type) {
547           LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small.";
548           return {};
549         }
550 
551         if (!VerifyResTableType(type)) {
552           return {};
553         }
554 
555         // Type chunks must be preceded by their TypeSpec chunks.
556         std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type->id];
557         if (builder_ptr != nullptr) {
558           builder_ptr->AddType(type.verified());
559         } else {
560           LOG(ERROR) << StringPrintf(
561               "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.",
562               type->id);
563           return {};
564         }
565       } break;
566 
567       case RES_TABLE_LIBRARY_TYPE: {
568         const auto lib = child_chunk.header<ResTable_lib_header>();
569         if (!lib) {
570           LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small.";
571           return {};
572         }
573 
574         if (child_chunk.data_size() / sizeof(ResTable_lib_entry) < dtohl(lib->count)) {
575           LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small to hold entries.";
576           return {};
577         }
578 
579         loaded_package->dynamic_package_map_.reserve(dtohl(lib->count));
580 
581         const auto entry_begin = child_chunk.data_ptr().convert<ResTable_lib_entry>();
582         const auto entry_end = entry_begin + dtohl(lib->count);
583         for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
584           if (!entry_iter) {
585             return {};
586           }
587 
588           std::string package_name;
589           util::ReadUtf16StringFromDevice(entry_iter->packageName,
590                                           arraysize(entry_iter->packageName), &package_name);
591 
592           if (dtohl(entry_iter->packageId) >= std::numeric_limits<uint8_t>::max()) {
593             LOG(ERROR) << StringPrintf(
594                 "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.",
595                 dtohl(entry_iter->packageId), package_name.c_str());
596             return {};
597           }
598 
599           loaded_package->dynamic_package_map_.emplace_back(std::move(package_name),
600                                                             dtohl(entry_iter->packageId));
601         }
602       } break;
603 
604       case RES_TABLE_OVERLAYABLE_TYPE: {
605         const auto overlayable = child_chunk.header<ResTable_overlayable_header>();
606         if (!overlayable) {
607           LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
608           return {};
609         }
610 
611         std::string name;
612         util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
613         std::string actor;
614         util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
615 
616         if (loaded_package->overlayable_map_.find(name) !=
617             loaded_package->overlayable_map_.end()) {
618           LOG(ERROR) << "Multiple <overlayable> blocks with the same name '" << name << "'.";
619           return {};
620         }
621         loaded_package->overlayable_map_.emplace(name, actor);
622 
623         // Iterate over the overlayable policy chunks contained within the overlayable chunk data
624         ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
625         while (overlayable_iter.HasNext()) {
626           const Chunk overlayable_child_chunk = overlayable_iter.Next();
627 
628           switch (overlayable_child_chunk.type()) {
629             case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
630               const auto policy_header =
631                   overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
632               if (!policy_header) {
633                 LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
634                 return {};
635               }
636 
637               if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
638                   < dtohl(policy_header->entry_count)) {
639                 LOG(ERROR) <<  "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
640                 return {};
641               }
642 
643               // Retrieve all the resource ids belonging to this policy chunk
644               std::unordered_set<uint32_t> ids;
645               const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>();
646               const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
647               for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
648                 if (!id_iter) {
649                   return {};
650                 }
651                 ids.insert(dtohl(id_iter->ident));
652               }
653 
654               // Add the pairing of overlayable properties and resource ids to the package
655               OverlayableInfo overlayable_info{};
656               overlayable_info.name = name;
657               overlayable_info.actor = actor;
658               overlayable_info.policy_flags = policy_header->policy_flags;
659               loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids);
660               loaded_package->defines_overlayable_ = true;
661               break;
662             }
663 
664             default:
665               LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
666               break;
667           }
668         }
669 
670         if (overlayable_iter.HadError()) {
671           LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_TYPE: %s",
672                                      overlayable_iter.GetLastError().c_str());
673           if (overlayable_iter.HadFatalError()) {
674             return {};
675           }
676         }
677       } break;
678 
679       case RES_TABLE_STAGED_ALIAS_TYPE: {
680         if (loaded_package->package_id_ != kFrameworkPackageId) {
681           LOG(WARNING) << "Alias chunk ignored for non-framework package '"
682                        << loaded_package->package_name_ << "'";
683           break;
684         }
685 
686         std::unordered_set<uint32_t> finalized_ids;
687         const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
688         if (!lib_alias) {
689           return {};
690         }
691         const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
692         const auto entry_end = entry_begin + dtohl(lib_alias->count);
693         for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
694           if (!entry_iter) {
695             return {};
696           }
697           auto finalized_id = dtohl(entry_iter->finalizedResId);
698           if (!finalized_ids.insert(finalized_id).second) {
699             LOG(ERROR) << StringPrintf("Repeated finalized resource id '%08x' in staged aliases.",
700                                        finalized_id);
701             return {};
702           }
703 
704           auto staged_id = dtohl(entry_iter->stagedResId);
705           auto [_, success] = loaded_package->alias_id_map_.insert(std::make_pair(staged_id,
706                                                                                   finalized_id));
707           if (!success) {
708             LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
709                                        staged_id);
710             return {};
711           }
712         }
713       } break;
714 
715       default:
716         LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
717         break;
718     }
719   }
720 
721   if (iter.HadError()) {
722     LOG(ERROR) << iter.GetLastError();
723     if (iter.HadFatalError()) {
724       return {};
725     }
726   }
727 
728   // Flatten and construct the TypeSpecs.
729   for (auto& entry : type_builder_map) {
730     TypeSpec type_spec = entry.second->Build();
731     uint8_t type_id = static_cast<uint8_t>(entry.first);
732     loaded_package->type_specs_[type_id] = std::move(type_spec);
733   }
734 
735   return std::move(loaded_package);
736 }
737 
LoadTable(const Chunk & chunk,const LoadedIdmap * loaded_idmap,package_property_t property_flags)738 bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
739                            package_property_t property_flags) {
740   incfs::map_ptr<ResTable_header> header = chunk.header<ResTable_header>();
741   if (!header) {
742     LOG(ERROR) << "RES_TABLE_TYPE too small.";
743     return false;
744   }
745 
746   if (loaded_idmap != nullptr) {
747     global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
748   }
749 
750   const size_t package_count = dtohl(header->packageCount);
751   size_t packages_seen = 0;
752 
753   packages_.reserve(package_count);
754 
755   ChunkIterator iter(chunk.data_ptr(), chunk.data_size());
756   while (iter.HasNext()) {
757     const Chunk child_chunk = iter.Next();
758     switch (child_chunk.type()) {
759       case RES_STRING_POOL_TYPE:
760         // Only use the first string pool. Ignore others.
761         if (global_string_pool_->getError() == NO_INIT) {
762           status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(),
763                                                     child_chunk.size());
764           if (err != NO_ERROR) {
765             LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt.";
766             return false;
767           }
768         } else {
769           LOG(WARNING) << "Multiple RES_STRING_POOL_TYPEs found in RES_TABLE_TYPE.";
770         }
771         break;
772 
773       case RES_TABLE_PACKAGE_TYPE: {
774         if (packages_seen + 1 > package_count) {
775           LOG(ERROR) << "More package chunks were found than the " << package_count
776                      << " declared in the header.";
777           return false;
778         }
779         packages_seen++;
780 
781         std::unique_ptr<const LoadedPackage> loaded_package =
782             LoadedPackage::Load(child_chunk, property_flags);
783         if (!loaded_package) {
784           return false;
785         }
786         packages_.push_back(std::move(loaded_package));
787       } break;
788 
789       default:
790         LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
791         break;
792     }
793   }
794 
795   if (iter.HadError()) {
796     LOG(ERROR) << iter.GetLastError();
797     if (iter.HadFatalError()) {
798       return false;
799     }
800   }
801   return true;
802 }
803 
Load(incfs::map_ptr<void> data,const size_t length,const LoadedIdmap * loaded_idmap,const package_property_t property_flags)804 std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
805                                              const size_t length,
806                                              const LoadedIdmap* loaded_idmap,
807                                              const package_property_t property_flags) {
808   ATRACE_NAME("LoadedArsc::Load");
809 
810   // Not using make_unique because the constructor is private.
811   std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
812 
813   ChunkIterator iter(data, length);
814   while (iter.HasNext()) {
815     const Chunk chunk = iter.Next();
816     switch (chunk.type()) {
817       case RES_TABLE_TYPE:
818         if (!loaded_arsc->LoadTable(chunk, loaded_idmap, property_flags)) {
819           return {};
820         }
821         break;
822 
823       default:
824         LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
825         break;
826     }
827   }
828 
829   if (iter.HadError()) {
830     LOG(ERROR) << iter.GetLastError();
831     if (iter.HadFatalError()) {
832       return {};
833     }
834   }
835 
836   return loaded_arsc;
837 }
838 
CreateEmpty()839 std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
840   return std::unique_ptr<LoadedArsc>(new LoadedArsc());
841 }
842 
843 }  // namespace android
844