• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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/Idmap.h"
20 
21 #include "android-base/logging.h"
22 #include "android-base/stringprintf.h"
23 #include "androidfw/misc.h"
24 #include "androidfw/ResourceTypes.h"
25 #include "androidfw/Util.h"
26 #include "utils/ByteOrder.h"
27 #include "utils/Trace.h"
28 
29 #ifdef _WIN32
30 #ifdef ERROR
31 #undef ERROR
32 #endif
33 #endif
34 
35 using ::android::base::StringPrintf;
36 
37 namespace android {
38 
compare_target_entries(const Idmap_target_entry & e1,const uint32_t target_id)39 static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
40   return dtohl(e1.target_id) < target_id;
41 }
42 
compare_overlay_entries(const Idmap_overlay_entry & e1,const uint32_t overlay_id)43 static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
44   return dtohl(e1.overlay_id) < overlay_id;
45 }
46 
Size() const47 size_t Idmap_header::Size() const {
48   return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size);
49 }
50 
OverlayStringPool(const LoadedIdmap * loaded_idmap)51 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
52     : data_header_(loaded_idmap->data_header_),
53       idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
54 
~OverlayStringPool()55 OverlayStringPool::~OverlayStringPool() {
56   uninit();
57 }
58 
stringAt(size_t idx,size_t * outLen) const59 const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
60   const size_t offset = dtohl(data_header_->string_pool_index_offset);
61   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
62     return idmap_string_pool_->stringAt(idx - offset, outLen);
63   }
64 
65   return ResStringPool::stringAt(idx, outLen);
66 }
67 
string8At(size_t idx,size_t * outLen) const68 const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
69   const size_t offset = dtohl(data_header_->string_pool_index_offset);
70   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
71     return idmap_string_pool_->string8At(idx - offset, outLen);
72   }
73 
74   return ResStringPool::string8At(idx, outLen);
75 }
76 
size() const77 size_t OverlayStringPool::size() const {
78   return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
79 }
80 
OverlayDynamicRefTable(const Idmap_data_header * data_header,const Idmap_overlay_entry * entries,uint8_t target_assigned_package_id)81 OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
82                                                const Idmap_overlay_entry* entries,
83                                                uint8_t target_assigned_package_id)
84     : data_header_(data_header),
85       entries_(entries),
86       target_assigned_package_id_(target_assigned_package_id) { };
87 
lookupResourceId(uint32_t * resId) const88 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
89   const Idmap_overlay_entry* first_entry = entries_;
90   const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
91   auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
92 
93   if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
94     // A mapping for the target resource id could not be found.
95     return DynamicRefTable::lookupResourceId(resId);
96   }
97 
98   *resId = (0x00FFFFFFU & dtohl(entry->target_id))
99       | (((uint32_t) target_assigned_package_id_) << 24);
100   return NO_ERROR;
101 }
102 
lookupResourceIdNoRewrite(uint32_t * resId) const103 status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
104   return DynamicRefTable::lookupResourceId(resId);
105 }
106 
IdmapResMap(const Idmap_data_header * data_header,const Idmap_target_entry * entries,uint8_t target_assigned_package_id,const OverlayDynamicRefTable * overlay_ref_table)107 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
108                          const Idmap_target_entry* entries,
109                          uint8_t target_assigned_package_id,
110                          const OverlayDynamicRefTable* overlay_ref_table)
111     : data_header_(data_header),
112       entries_(entries),
113       target_assigned_package_id_(target_assigned_package_id),
114       overlay_ref_table_(overlay_ref_table) { };
115 
Lookup(uint32_t target_res_id) const116 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
117   if ((target_res_id >> 24) != target_assigned_package_id_) {
118     // The resource id must have the same package id as the target package.
119     return {};
120   }
121 
122   // The resource ids encoded within the idmap are build-time resource ids.
123   target_res_id = (0x00FFFFFFU & target_res_id)
124       | (((uint32_t) data_header_->target_package_id) << 24);
125 
126   const Idmap_target_entry* first_entry = entries_;
127   const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
128   auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
129 
130   if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
131     // A mapping for the target resource id could not be found.
132     return {};
133   }
134 
135   // A reference should be treated as an alias of the resource. Instead of returning the table
136   // entry, return the alias resource id to look up. The alias resource might not reside within the
137   // overlay package, so the resource id must be fixed with the dynamic reference table of the
138   // overlay before returning.
139   if (entry->type == Res_value::TYPE_REFERENCE
140       || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
141     uint32_t overlay_resource_id = dtohl(entry->value);
142 
143     // Lookup the resource without rewriting the overlay resource id back to the target resource id
144     // being looked up.
145     overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
146     return Result(overlay_resource_id);
147   }
148 
149   // Copy the type and value into the ResTable_entry structure needed by asset manager.
150   uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
151   auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
152   memset(table_entry, 0, malloc_size);
153   table_entry->size = htods(sizeof(ResTable_entry));
154 
155   auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
156       + sizeof(ResTable_entry));
157   table_value->dataType = entry->type;
158   table_value->data = entry->value;
159 
160   return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
161 }
162 
is_word_aligned(const void * data)163 static bool is_word_aligned(const void* data) {
164   return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
165 }
166 
IsValidIdmapHeader(const StringPiece & data)167 static bool IsValidIdmapHeader(const StringPiece& data) {
168   if (!is_word_aligned(data.data())) {
169     LOG(ERROR) << "Idmap header is not word aligned.";
170     return false;
171   }
172 
173   if (data.size() < sizeof(Idmap_header)) {
174     LOG(ERROR) << "Idmap header is too small.";
175     return false;
176   }
177 
178   const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
179   if (dtohl(header->magic) != kIdmapMagic) {
180     LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
181                                dtohl(header->magic), kIdmapMagic);
182     return false;
183   }
184 
185   if (dtohl(header->version) != kIdmapCurrentVersion) {
186     // We are strict about versions because files with this format are auto-generated and don't need
187     // backwards compatibility.
188     LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
189                                dtohl(header->version), kIdmapCurrentVersion);
190     return false;
191   }
192 
193   return true;
194 }
195 
LoadedIdmap(std::string && idmap_path,const time_t last_mod_time,const Idmap_header * header,const Idmap_data_header * data_header,const Idmap_target_entry * target_entries,const Idmap_overlay_entry * overlay_entries,ResStringPool * string_pool)196 LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
197                          const time_t last_mod_time,
198                          const Idmap_header* header,
199                          const Idmap_data_header* data_header,
200                          const Idmap_target_entry* target_entries,
201                          const Idmap_overlay_entry* overlay_entries,
202                          ResStringPool* string_pool)
203      : header_(header),
204        data_header_(data_header),
205        target_entries_(target_entries),
206        overlay_entries_(overlay_entries),
207        string_pool_(string_pool),
208        idmap_path_(std::move(idmap_path)),
209        idmap_last_mod_time_(last_mod_time) {
210 
211   size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
212                           arraysize(header_->overlay_path));
213   overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
214 
215   length = strnlen(reinterpret_cast<const char*>(header_->target_path),
216                           arraysize(header_->target_path));
217   target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
218 }
219 
Load(const StringPiece & idmap_path,const StringPiece & idmap_data)220 std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
221                                                      const StringPiece& idmap_data) {
222   ATRACE_CALL();
223   if (!IsValidIdmapHeader(idmap_data)) {
224     return {};
225   }
226 
227   auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
228   const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size();
229   size_t data_size = idmap_data.size() - header->Size();
230 
231   // Currently idmap2 can only generate one data block.
232   auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
233   data_ptr += sizeof(*data_header);
234   data_size -= sizeof(*data_header);
235 
236   // Make sure there is enough space for the target entries declared in the header.
237   const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
238   if (data_size / sizeof(Idmap_target_entry) <
239       static_cast<size_t>(dtohl(data_header->target_entry_count))) {
240     LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
241                                (int)dtohl(data_header->target_entry_count));
242     return {};
243   }
244 
245   // Advance the data pointer past the target entries.
246   const size_t target_entry_size_bytes =
247       (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
248   data_ptr += target_entry_size_bytes;
249   data_size -= target_entry_size_bytes;
250 
251   // Make sure there is enough space for the overlay entries declared in the header.
252   const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
253   if (data_size / sizeof(Idmap_overlay_entry) <
254       static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
255     LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
256                                (int)dtohl(data_header->overlay_entry_count));
257     return {};
258   }
259 
260   // Advance the data pointer past the target entries.
261   const size_t overlay_entry_size_bytes =
262       (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
263   data_ptr += overlay_entry_size_bytes;
264   data_size -= overlay_entry_size_bytes;
265 
266   // Read the idmap string pool that holds the value of inline string entries.
267   if (data_size < dtohl(data_header->string_pool_length)) {
268     LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
269                                (int)dtohl(data_header->string_pool_length));
270     return {};
271   }
272 
273   auto idmap_string_pool = util::make_unique<ResStringPool>();
274   if (dtohl(data_header->string_pool_length) > 0) {
275     status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
276     if (err != NO_ERROR) {
277       LOG(ERROR) << "idmap string pool corrupt.";
278       return {};
279     }
280   }
281 
282   // Can't use make_unique because LoadedIdmap constructor is private.
283   std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
284       new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header,
285                       data_header, target_entries, overlay_entries, idmap_string_pool.release()));
286 
287   return std::move(loaded_idmap);
288 }
289 
IsUpToDate() const290 bool LoadedIdmap::IsUpToDate() const {
291   return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
292 }
293 
294 }  // namespace android
295