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