Home | History | Annotate | Download | only in androidfw
      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