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 "utils/ByteOrder.h"
24 #include "utils/Trace.h"
25
26 #ifdef _WIN32
27 #ifdef ERROR
28 #undef ERROR
29 #endif
30 #endif
31
32 #include "androidfw/ResourceTypes.h"
33
34 using ::android::base::StringPrintf;
35
36 namespace android {
37
is_valid_package_id(uint16_t id)38 constexpr static inline bool is_valid_package_id(uint16_t id) {
39 return id != 0 && id <= 255;
40 }
41
is_valid_type_id(uint16_t id)42 constexpr static inline bool is_valid_type_id(uint16_t id) {
43 // Type IDs and package IDs have the same constraints in the IDMAP.
44 return is_valid_package_id(id);
45 }
46
Lookup(const IdmapEntry_header * header,uint16_t input_entry_id,uint16_t * output_entry_id)47 bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
48 uint16_t* output_entry_id) {
49 if (input_entry_id < dtohs(header->entry_id_offset)) {
50 // After applying the offset, the entry is not present.
51 return false;
52 }
53
54 input_entry_id -= dtohs(header->entry_id_offset);
55 if (input_entry_id >= dtohs(header->entry_count)) {
56 // The entry is not present.
57 return false;
58 }
59
60 uint32_t result = dtohl(header->entries[input_entry_id]);
61 if (result == 0xffffffffu) {
62 return false;
63 }
64 *output_entry_id = static_cast<uint16_t>(result);
65 return true;
66 }
67
is_word_aligned(const void * data)68 static bool is_word_aligned(const void* data) {
69 return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
70 }
71
IsValidIdmapHeader(const StringPiece & data)72 static bool IsValidIdmapHeader(const StringPiece& data) {
73 if (!is_word_aligned(data.data())) {
74 LOG(ERROR) << "Idmap header is not word aligned.";
75 return false;
76 }
77
78 if (data.size() < sizeof(Idmap_header)) {
79 LOG(ERROR) << "Idmap header is too small.";
80 return false;
81 }
82
83 const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
84 if (dtohl(header->magic) != kIdmapMagic) {
85 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
86 dtohl(header->magic), kIdmapMagic);
87 return false;
88 }
89
90 if (dtohl(header->version) != kIdmapCurrentVersion) {
91 // We are strict about versions because files with this format are auto-generated and don't need
92 // backwards compatibility.
93 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
94 dtohl(header->version), kIdmapCurrentVersion);
95 return false;
96 }
97
98 if (!is_valid_package_id(dtohs(header->target_package_id))) {
99 LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x",
100 dtohs(header->target_package_id));
101 return false;
102 }
103
104 if (dtohs(header->type_count) > 255) {
105 LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)",
106 (int)dtohs(header->type_count));
107 return false;
108 }
109 return true;
110 }
111
LoadedIdmap(const Idmap_header * header)112 LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) {
113 size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
114 arraysize(header_->overlay_path));
115 overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
116 }
117
Load(const StringPiece & idmap_data)118 std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
119 ATRACE_CALL();
120 if (!IsValidIdmapHeader(idmap_data)) {
121 return {};
122 }
123
124 const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
125
126 // Can't use make_unique because LoadedImpl constructor is private.
127 std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header));
128
129 const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header);
130 size_t data_size = idmap_data.size() - sizeof(*header);
131
132 size_t type_maps_encountered = 0u;
133 while (data_size >= sizeof(IdmapEntry_header)) {
134 if (!is_word_aligned(data_ptr)) {
135 LOG(ERROR) << "Type mapping in Idmap is not word aligned";
136 return {};
137 }
138
139 // Validate the type IDs.
140 const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr);
141 if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) {
142 LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)",
143 dtohs(entry_header->target_type_id),
144 dtohs(entry_header->overlay_type_id));
145 return {};
146 }
147
148 // Make sure there is enough space for the entries declared in the header.
149 if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) <
150 static_cast<size_t>(dtohs(entry_header->entry_count))) {
151 LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)",
152 (int)dtohs(entry_header->entry_count));
153 return {};
154 }
155
156 // Only add a non-empty overlay.
157 if (dtohs(entry_header->entry_count != 0)) {
158 loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] =
159 entry_header;
160 }
161
162 const size_t entry_size_bytes =
163 sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t));
164 data_ptr += entry_size_bytes;
165 data_size -= entry_size_bytes;
166 type_maps_encountered++;
167 }
168
169 // Verify that we parsed all the type maps.
170 if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) {
171 LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected "
172 << (int)dtohs(header->type_count);
173 return {};
174 }
175 return std::move(loaded_idmap);
176 }
177
TargetPackageId() const178 uint8_t LoadedIdmap::TargetPackageId() const {
179 return static_cast<uint8_t>(dtohs(header_->target_package_id));
180 }
181
GetEntryMapForType(uint8_t type_id) const182 const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const {
183 auto iter = type_map_.find(type_id);
184 if (iter != type_map_.end()) {
185 return iter->second;
186 }
187 return nullptr;
188 }
189
190 } // namespace android
191