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 
39 // See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification.
40 struct Idmap_header {
41   // Always 0x504D4449 ('IDMP')
42   uint32_t magic;
43   uint32_t version;
44 
45   uint32_t target_crc32;
46   uint32_t overlay_crc32;
47 
48   uint32_t fulfilled_policies;
49   uint32_t enforce_overlayable;
50 
51   // overlay_path, target_path, and other string values encoded in the idmap header and read and
52   // stored in separate structures. This allows the idmap header data to be casted to this struct
53   // without having to read/store each header entry separately.
54 };
55 
56 struct Idmap_data_header {
57   uint32_t target_entry_count;
58   uint32_t target_inline_entry_count;
59   uint32_t overlay_entry_count;
60 
61   uint32_t string_pool_index_offset;
62 };
63 
64 struct Idmap_target_entry {
65   uint32_t target_id;
66   uint32_t overlay_id;
67 };
68 
69 struct Idmap_target_entry_inline {
70   uint32_t target_id;
71   Res_value value;
72 };
73 
74 struct Idmap_overlay_entry {
75   uint32_t overlay_id;
76   uint32_t target_id;
77 };
78 
79 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
80     : data_header_(loaded_idmap->data_header_),
81       idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
82 
83 OverlayStringPool::~OverlayStringPool() {
84   uninit();
85 }
86 
87 base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
88   const size_t offset = dtohl(data_header_->string_pool_index_offset);
89   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
90     return idmap_string_pool_->stringAt(idx - offset);
91   }
92 
93   return ResStringPool::stringAt(idx);
94 }
95 
96 base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
97   const size_t offset = dtohl(data_header_->string_pool_index_offset);
98   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
99     return idmap_string_pool_->string8At(idx - offset);
100   }
101 
102   return ResStringPool::string8At(idx);
103 }
104 
105 size_t OverlayStringPool::size() const {
106   return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
107 }
108 
109 OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
110                                                const Idmap_overlay_entry* entries,
111                                                uint8_t target_assigned_package_id)
112     : data_header_(data_header),
113       entries_(entries),
114       target_assigned_package_id_(target_assigned_package_id) { };
115 
116 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
117   const Idmap_overlay_entry* first_entry = entries_;
118   const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
119   auto entry = std::lower_bound(first_entry, end_entry, *resId,
120                                 [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
121     return dtohl(e1.overlay_id) < overlay_id;
122   });
123 
124   if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
125     // A mapping for the target resource id could not be found.
126     return DynamicRefTable::lookupResourceId(resId);
127   }
128 
129   *resId = (0x00FFFFFFU & dtohl(entry->target_id))
130       | (((uint32_t) target_assigned_package_id_) << 24U);
131   return NO_ERROR;
132 }
133 
134 status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
135   return DynamicRefTable::lookupResourceId(resId);
136 }
137 
138 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
139                          const Idmap_target_entry* entries,
140                          const Idmap_target_entry_inline* inline_entries,
141                          uint8_t target_assigned_package_id,
142                          const OverlayDynamicRefTable* overlay_ref_table)
143     : data_header_(data_header),
144       entries_(entries),
145       inline_entries_(inline_entries),
146       target_assigned_package_id_(target_assigned_package_id),
147       overlay_ref_table_(overlay_ref_table) { }
148 
149 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
150   if ((target_res_id >> 24U) != target_assigned_package_id_) {
151     // The resource id must have the same package id as the target package.
152     return {};
153   }
154 
155   // The resource ids encoded within the idmap are build-time resource ids so do not consider the
156   // package id when determining if the resource in the target package is overlaid.
157   target_res_id &= 0x00FFFFFFU;
158 
159   // Check if the target resource is mapped to an overlay resource.
160   auto first_entry = entries_;
161   auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
162   auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
163                                 [](const Idmap_target_entry& e, const uint32_t target_id) {
164     return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
165   });
166 
167   if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) {
168     uint32_t overlay_resource_id = dtohl(entry->overlay_id);
169     // Lookup the resource without rewriting the overlay resource id back to the target resource id
170     // being looked up.
171     overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
172     return Result(overlay_resource_id);
173   }
174 
175   // Check if the target resources is mapped to an inline table entry.
176   auto first_inline_entry = inline_entries_;
177   auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
178   auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
179                                        [](const Idmap_target_entry_inline& e,
180                                           const uint32_t target_id) {
181     return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
182   });
183 
184   if (inline_entry != end_inline_entry &&
185       (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
186     return Result(inline_entry->value);
187   }
188   return {};
189 }
190 
191 namespace {
192 template <typename T>
193 const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label,
194                   size_t count = 1) {
195   if (!util::IsFourByteAligned(*in_out_data_ptr)) {
196     LOG(ERROR) << "Idmap " << label << " is not word aligned.";
197     return {};
198   }
199   if ((*in_out_size / sizeof(T)) < count) {
200     LOG(ERROR) << "Idmap too small for the number of " << label << " entries ("
201                << count << ").";
202     return nullptr;
203   }
204   auto data_ptr = *in_out_data_ptr;
205   const size_t read_size = sizeof(T) * count;
206   *in_out_data_ptr += read_size;
207   *in_out_size -= read_size;
208   return reinterpret_cast<const T*>(data_ptr);
209 }
210 
211 std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
212                                            const std::string& label) {
213   const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length");
214   if (len == nullptr) {
215     return {};
216   }
217   const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len);
218   if (data == nullptr) {
219     return {};
220   }
221   // Strings are padded to the next 4 byte boundary.
222   const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
223   for (uint32_t i = 0; i < padding_size; i++) {
224     if (**in_out_data_ptr != 0) {
225       LOG(ERROR) << " Idmap padding of " << label << " is non-zero.";
226       return {};
227     }
228     *in_out_data_ptr += sizeof(uint8_t);
229     *in_out_size -= sizeof(uint8_t);
230   }
231   return std::string_view(data, *len);
232 }
233 } // namespace
234 
235 LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
236                          const Idmap_header* header,
237                          const Idmap_data_header* data_header,
238                          const Idmap_target_entry* target_entries,
239                          const Idmap_target_entry_inline* target_inline_entries,
240                          const Idmap_overlay_entry* overlay_entries,
241                          std::unique_ptr<ResStringPool>&& string_pool,
242                          std::string_view overlay_apk_path,
243                          std::string_view target_apk_path)
244      : header_(header),
245        data_header_(data_header),
246        target_entries_(target_entries),
247        target_inline_entries_(target_inline_entries),
248        overlay_entries_(overlay_entries),
249        string_pool_(std::move(string_pool)),
250        idmap_path_(std::move(idmap_path)),
251        overlay_apk_path_(overlay_apk_path),
252        target_apk_path_(target_apk_path),
253        idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
254 
255 std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
256                                                const StringPiece& idmap_data) {
257   ATRACE_CALL();
258   size_t data_size = idmap_data.size();
259   auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
260 
261   // Parse the idmap header
262   auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header");
263   if (header == nullptr) {
264     return {};
265   }
266   if (dtohl(header->magic) != kIdmapMagic) {
267     LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
268                                dtohl(header->magic), kIdmapMagic);
269     return {};
270   }
271   if (dtohl(header->version) != kIdmapCurrentVersion) {
272     // We are strict about versions because files with this format are generated at runtime and
273     // don't need backwards compatibility.
274     LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
275                                dtohl(header->version), kIdmapCurrentVersion);
276     return {};
277   }
278   std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
279   if (!overlay_path) {
280     return {};
281   }
282   std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
283   if (!target_path) {
284     return {};
285   }
286   if (!ReadString(&data_ptr, &data_size, "target name") ||
287       !ReadString(&data_ptr, &data_size, "debug info")) {
288     return {};
289   }
290 
291   // Parse the idmap data blocks. Currently idmap2 can only generate one data block.
292   auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
293   if (data_header == nullptr) {
294     return {};
295   }
296   auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target",
297                                                      dtohl(data_header->target_entry_count));
298   if (target_entries == nullptr) {
299     return {};
300   }
301   auto target_inline_entries = ReadType<Idmap_target_entry_inline>(
302       &data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count));
303   if (target_inline_entries == nullptr) {
304     return {};
305   }
306   auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
307                                                        dtohl(data_header->overlay_entry_count));
308   if (overlay_entries == nullptr) {
309     return {};
310   }
311   std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
312   if (!string_pool) {
313     return {};
314   }
315   auto idmap_string_pool = util::make_unique<ResStringPool>();
316   if (!string_pool->empty()) {
317     const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size());
318     if (err != NO_ERROR) {
319       LOG(ERROR) << "idmap string pool corrupt.";
320       return {};
321     }
322   }
323 
324   if (data_size != 0) {
325     LOG(ERROR) << "idmap parsed with " << data_size << "bytes remaining";
326     return {};
327   }
328 
329   // Can't use make_unique because LoadedIdmap constructor is private.
330   return std::unique_ptr<LoadedIdmap>(
331       new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
332                       target_inline_entries, overlay_entries, std::move(idmap_string_pool),
333                       *target_path, *overlay_path));
334 }
335 
336 bool LoadedIdmap::IsUpToDate() const {
337   return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
338 }
339 
340 }  // namespace android
341