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 38 constexpr static inline bool is_valid_package_id(uint16_t id) { 39 return id != 0 && id <= 255; 40 } 41 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 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 68 static bool is_word_aligned(const void* data) { 69 return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0; 70 } 71 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 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 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 178 uint8_t LoadedIdmap::TargetPackageId() const { 179 return static_cast<uint8_t>(dtohs(header_->target_package_id)); 180 } 181 182 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