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