• 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/file.h"
22 #include "android-base/logging.h"
23 #include "android-base/stringprintf.h"
24 #include "android-base/utf8.h"
25 #include "androidfw/AssetManager.h"
26 #include "androidfw/ResourceTypes.h"
27 #include "androidfw/Util.h"
28 #include "androidfw/misc.h"
29 #include "utils/ByteOrder.h"
30 #include "utils/Trace.h"
31 
32 #ifdef _WIN32
33 #ifdef ERROR
34 #undef ERROR
35 #endif
36 #endif
37 
38 using ::android::base::StringPrintf;
39 
40 namespace android {
41 
42 // See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification.
43 struct Idmap_header {
44   // Always 0x504D4449 ('IDMP')
45   uint32_t magic;
46   uint32_t version;
47 
48   uint32_t target_crc32;
49   uint32_t overlay_crc32;
50 
51   uint32_t fulfilled_policies;
52   uint32_t enforce_overlayable;
53 
54   // overlay_path, target_path, and other string values encoded in the idmap header and read and
55   // stored in separate structures. This allows the idmap header data to be casted to this struct
56   // without having to read/store each header entry separately.
57 };
58 
59 struct Idmap_data_header {
60   uint32_t target_entry_count;
61   uint32_t target_inline_entry_count;
62   uint32_t target_inline_entry_value_count;
63   uint32_t configuration_count;
64   uint32_t overlay_entry_count;
65 
66   uint32_t string_pool_index_offset;
67 };
68 
69 struct Idmap_target_entry_inline {
70   uint32_t start_value_index;
71   uint32_t value_count;
72 };
73 
74 struct Idmap_target_entry_inline_value {
75   uint32_t config_index;
76   Res_value value;
77 };
78 
convert_dev_target_id(uint32_t dev_target_id)79 static constexpr uint32_t convert_dev_target_id(uint32_t dev_target_id) {
80   return (0x00FFFFFFU & dtohl(dev_target_id));
81 }
82 
OverlayStringPool(const LoadedIdmap * loaded_idmap)83 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
84     : data_header_(loaded_idmap->data_header_),
85       idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
86 
~OverlayStringPool()87 OverlayStringPool::~OverlayStringPool() {
88   uninit();
89 }
90 
stringAt(size_t idx) const91 base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
92   const size_t offset = dtohl(data_header_->string_pool_index_offset);
93   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
94     return idmap_string_pool_->stringAt(idx - offset);
95   }
96 
97   return ResStringPool::stringAt(idx);
98 }
99 
string8At(size_t idx) const100 base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
101   const size_t offset = dtohl(data_header_->string_pool_index_offset);
102   if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
103     return idmap_string_pool_->string8At(idx - offset);
104   }
105 
106   return ResStringPool::string8At(idx);
107 }
108 
size() const109 size_t OverlayStringPool::size() const {
110   return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
111 }
112 
OverlayDynamicRefTable(const Idmap_data_header * data_header,Idmap_overlay_entries entries,uint8_t target_assigned_package_id)113 OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
114                                                Idmap_overlay_entries entries,
115                                                uint8_t target_assigned_package_id)
116     : data_header_(data_header),
117       entries_(entries),
118       target_assigned_package_id_(target_assigned_package_id) {
119 }
120 
lookupResourceId(uint32_t * resId) const121 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
122   const auto count = dtohl(data_header_->overlay_entry_count);
123   const auto overlay_it_end = entries_.overlay_id + count;
124   const auto entry_it = std::lower_bound(entries_.overlay_id, overlay_it_end, *resId,
125                                          [](uint32_t dev_overlay_id, uint32_t overlay_id) {
126                                            return dtohl(dev_overlay_id) < overlay_id;
127                                          });
128 
129   if (entry_it == overlay_it_end || dtohl(*entry_it) != *resId) {
130     // A mapping for the target resource id could not be found.
131     return DynamicRefTable::lookupResourceId(resId);
132   }
133 
134   const auto index = entry_it - entries_.overlay_id;
135   *resId = convert_dev_target_id(entries_.target_id[index]) |
136            (((uint32_t)target_assigned_package_id_) << 24U);
137   return NO_ERROR;
138 }
139 
lookupResourceIdNoRewrite(uint32_t * resId) const140 status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
141   return DynamicRefTable::lookupResourceId(resId);
142 }
143 
IdmapResMap(const Idmap_data_header * data_header,const Idmap_constraints & constraints,Idmap_target_entries entries,Idmap_target_inline_entries inline_entries,const Idmap_target_entry_inline_value * inline_entry_values,const ConfigDescription * configs,uint8_t target_assigned_package_id,const OverlayDynamicRefTable * overlay_ref_table)144 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, const Idmap_constraints& constraints,
145                          Idmap_target_entries entries, Idmap_target_inline_entries inline_entries,
146                          const Idmap_target_entry_inline_value* inline_entry_values,
147                          const ConfigDescription* configs, uint8_t target_assigned_package_id,
148                          const OverlayDynamicRefTable* overlay_ref_table)
149     : data_header_(data_header),
150       constraints_(constraints),
151       entries_(entries),
152       inline_entries_(inline_entries),
153       inline_entry_values_(inline_entry_values),
154       configurations_(configs),
155       target_assigned_package_id_(target_assigned_package_id),
156       overlay_ref_table_(overlay_ref_table) {
157 }
158 
Lookup(uint32_t target_res_id) const159 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
160   if ((target_res_id >> 24U) != target_assigned_package_id_) {
161     // The resource id must have the same package id as the target package.
162     return {};
163   }
164 
165   // The resource ids encoded within the idmap are build-time resource ids so do not consider the
166   // package id when determining if the resource in the target package is overlaid.
167   target_res_id &= 0x00FFFFFFU;
168 
169   // Check if the target resource is mapped to an overlay resource.
170   const auto target_end = entries_.target_id + dtohl(data_header_->target_entry_count);
171   auto target_it = std::lower_bound(entries_.target_id, target_end, target_res_id,
172                                     [](uint32_t dev_target_id, uint32_t target_id) {
173                                       return convert_dev_target_id(dev_target_id) < target_id;
174                                     });
175 
176   if (target_it != target_end && convert_dev_target_id(*target_it) == target_res_id) {
177     const auto index = target_it - entries_.target_id;
178     uint32_t overlay_resource_id = dtohl(entries_.overlay_id[index]);
179     // Lookup the resource without rewriting the overlay resource id back to the target resource id
180     // being looked up.
181     overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
182     return Result(overlay_resource_id);
183   }
184 
185   // Check if the target resources is mapped to an inline table entry.
186   const auto inline_entry_target_end =
187       inline_entries_.target_id + dtohl(data_header_->target_inline_entry_count);
188   const auto inline_entry_target_it =
189       std::lower_bound(inline_entries_.target_id, inline_entry_target_end, target_res_id,
190                        [](uint32_t dev_target_id, uint32_t target_id) {
191                          return convert_dev_target_id(dev_target_id) < target_id;
192                        });
193 
194   if (inline_entry_target_it != inline_entry_target_end &&
195       convert_dev_target_id(*inline_entry_target_it) == target_res_id) {
196     const auto index = inline_entry_target_it - inline_entries_.target_id;
197     std::map<ConfigDescription, Res_value> values_map;
198     const auto& inline_entry = inline_entries_.entry[index];
199     for (int i = 0; i < dtohl(inline_entry.value_count); i++) {
200       const auto& value = inline_entry_values_[dtohl(inline_entry.start_value_index) + i];
201       const auto& config = configurations_[dtohl(value.config_index)];
202       values_map[config] = value.value;
203     }
204     return Result(std::move(values_map));
205   }
206   return {};
207 }
208 
209 namespace {
210 template <typename T>
ReadType(const uint8_t ** in_out_data_ptr,size_t * in_out_size,const char * label,size_t count=1)211 const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const char* label,
212                   size_t count = 1) {
213   if (!util::IsFourByteAligned(*in_out_data_ptr)) {
214     LOG(ERROR) << "Idmap " << label << " in " << __func__ << " is not word aligned.";
215     return {};
216   }
217   if ((*in_out_size / sizeof(T)) < count) {
218     LOG(ERROR) << "Idmap too small for the number of " << label << " in " << __func__
219                << " entries (" << count << ").";
220     return nullptr;
221   }
222   auto data_ptr = *in_out_data_ptr;
223   const size_t read_size = sizeof(T) * count;
224   *in_out_data_ptr += read_size;
225   *in_out_size -= read_size;
226   return reinterpret_cast<const T*>(data_ptr);
227 }
228 
ReadString(const uint8_t ** in_out_data_ptr,size_t * in_out_size,const char * label)229 std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
230                                            const char* label) {
231   const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label);
232   if (len == nullptr) {
233     return {};
234   }
235   const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len);
236   if (data == nullptr) {
237     return {};
238   }
239   // Strings are padded to the next 4 byte boundary.
240   const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
241   for (uint32_t i = 0; i < padding_size; i++) {
242     if (**in_out_data_ptr != 0) {
243       LOG(ERROR) << " Idmap padding of " << label << " in " << __func__ << " is non-zero.";
244       return {};
245     }
246     *in_out_data_ptr += sizeof(uint8_t);
247     *in_out_size -= sizeof(uint8_t);
248   }
249   return std::string_view(data, *len);
250 }
251 }  // namespace
252 
253 // O_PATH is a lightweight way of creating an FD, only exists on Linux
254 #ifndef O_PATH
255 #define O_PATH (0)
256 #endif
257 
LoadedIdmap(const std::string & idmap_path,const Idmap_header * header,const Idmap_data_header * data_header,const Idmap_constraints & constraints,Idmap_target_entries target_entries,Idmap_target_inline_entries target_inline_entries,const Idmap_target_entry_inline_value * inline_entry_values,const ConfigDescription * configs,Idmap_overlay_entries overlay_entries,std::unique_ptr<ResStringPool> && string_pool,std::string_view overlay_apk_path,std::string_view target_apk_path)258 LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
259                          const Idmap_data_header* data_header, const Idmap_constraints& constraints,
260                          Idmap_target_entries target_entries,
261                          Idmap_target_inline_entries target_inline_entries,
262                          const Idmap_target_entry_inline_value* inline_entry_values,
263                          const ConfigDescription* configs, Idmap_overlay_entries overlay_entries,
264                          std::unique_ptr<ResStringPool>&& string_pool,
265                          std::string_view overlay_apk_path, std::string_view target_apk_path)
266     : header_(header),
267       data_header_(data_header),
268       constraints_(constraints),
269       target_entries_(target_entries),
270       target_inline_entries_(target_inline_entries),
271       inline_entry_values_(inline_entry_values),
272       configurations_(configs),
273       overlay_entries_(overlay_entries),
274       string_pool_(std::move(string_pool)),
275       overlay_apk_path_(overlay_apk_path),
276       target_apk_path_(target_apk_path),
277       idmap_last_mod_time_(kInvalidModDate) {
278   if (!isReadonlyFilesystem(std::string(overlay_apk_path_).c_str()) ||
279       !(target_apk_path_ == AssetManager::TARGET_APK_PATH ||
280         isReadonlyFilesystem(std::string(target_apk_path_).c_str()))) {
281     idmap_fd_.reset(
282         android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH));
283     idmap_last_mod_time_ = getFileModDate(idmap_fd_);
284   }
285 }
286 
Load(StringPiece idmap_path,StringPiece idmap_data)287 std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
288   ATRACE_CALL();
289   size_t data_size = idmap_data.size();
290   auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
291 
292   // Parse the idmap header
293   auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header");
294   if (header == nullptr) {
295     return {};
296   }
297   if (dtohl(header->magic) != kIdmapMagic) {
298     LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
299                                dtohl(header->magic), kIdmapMagic);
300     return {};
301   }
302   if (dtohl(header->version) != kIdmapCurrentVersion) {
303     // We are strict about versions because files with this format are generated at runtime and
304     // don't need backwards compatibility.
305     LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
306                                dtohl(header->version), kIdmapCurrentVersion);
307     return {};
308   }
309   std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
310   if (!target_path) {
311     return {};
312   }
313   std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
314   if (!overlay_path) {
315     return {};
316   }
317   if (!ReadString(&data_ptr, &data_size, "target name") ||
318       !ReadString(&data_ptr, &data_size, "debug info")) {
319     return {};
320   }
321 
322   auto constraint_count = ReadType<uint32_t>(&data_ptr, &data_size, "constraint count");
323   if (!constraint_count) {
324     LOG(ERROR) << "idmap doesn't have constraint count";
325     return {};
326   }
327   auto constraint_entries = *constraint_count > 0 ?
328       ReadType<Idmap_constraint>(&data_ptr, &data_size, "constraints", dtohl(*constraint_count))
329       : nullptr;
330   if (*constraint_count > 0 && !constraint_entries) {
331     LOG(ERROR) << "no constraint entries in idmap with non-zero constraints";
332     return {};
333   }
334   Idmap_constraints constraints{.constraint_count = *constraint_count,
335                                 .constraint_entries = constraint_entries};
336 
337   // Parse the idmap data blocks. Currently idmap2 can only generate one data block.
338   auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
339   if (data_header == nullptr) {
340     return {};
341   }
342   Idmap_target_entries target_entries{
343       .target_id = ReadType<uint32_t>(&data_ptr, &data_size, "entries.target_id",
344                                       dtohl(data_header->target_entry_count)),
345       .overlay_id = ReadType<uint32_t>(&data_ptr, &data_size, "entries.overlay_id",
346                                        dtohl(data_header->target_entry_count)),
347   };
348   if (!target_entries.target_id || !target_entries.overlay_id) {
349     return {};
350   }
351   Idmap_target_inline_entries target_inline_entries{
352       .target_id = ReadType<uint32_t>(&data_ptr, &data_size, "target inline.target_id",
353                                       dtohl(data_header->target_inline_entry_count)),
354       .entry = ReadType<Idmap_target_entry_inline>(&data_ptr, &data_size, "target inline.entry",
355                                                    dtohl(data_header->target_inline_entry_count))};
356   if (!target_inline_entries.target_id || !target_inline_entries.entry) {
357     return {};
358   }
359 
360   auto target_inline_entry_values = ReadType<Idmap_target_entry_inline_value>(
361       &data_ptr, &data_size, "target inline values",
362       dtohl(data_header->target_inline_entry_value_count));
363   if (target_inline_entry_values == nullptr) {
364     return {};
365   }
366 
367   auto configurations = ReadType<ConfigDescription>(
368       &data_ptr, &data_size, "configurations",
369       dtohl(data_header->configuration_count));
370   if (configurations == nullptr) {
371     return {};
372   }
373 
374   Idmap_overlay_entries overlay_entries{
375       .overlay_id = ReadType<uint32_t>(&data_ptr, &data_size, "overlay entries.overlay_id",
376                                        dtohl(data_header->overlay_entry_count)),
377       .target_id = ReadType<uint32_t>(&data_ptr, &data_size, "overlay entries.target_id",
378                                       dtohl(data_header->overlay_entry_count)),
379   };
380   if (!overlay_entries.overlay_id || !overlay_entries.target_id) {
381     return {};
382   }
383   std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
384   if (!string_pool) {
385     return {};
386   }
387   auto idmap_string_pool = util::make_unique<ResStringPool>();
388   if (!string_pool->empty()) {
389     const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size());
390     if (err != NO_ERROR) {
391       LOG(ERROR) << "idmap string pool corrupt.";
392       return {};
393     }
394   }
395 
396   if (data_size != 0) {
397     LOG(ERROR) << "idmap parsed with " << data_size << "bytes remaining";
398     return {};
399   }
400 
401   // Can't use make_unique because LoadedIdmap constructor is private.
402   return std::unique_ptr<LoadedIdmap>(
403       new LoadedIdmap(std::string(idmap_path), header, data_header, constraints, target_entries,
404                       target_inline_entries, target_inline_entry_values, configurations,
405                       overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path));
406 }
407 
IsUpToDate() const408 UpToDate LoadedIdmap::IsUpToDate() const {
409   if (idmap_last_mod_time_ == kInvalidModDate) {
410     return UpToDate::Always;
411   }
412   return fromBool(idmap_last_mod_time_ == getFileModDate(idmap_fd_.get()));
413 }
414 
415 }  // namespace android
416