• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 #include "idmap2/ResourceMapping.h"
18 
19 #include <map>
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "android-base/stringprintf.h"
27 #include "androidfw/ResourceTypes.h"
28 #include "idmap2/PolicyUtils.h"
29 #include "idmap2/ResourceUtils.h"
30 
31 using android::base::StringPrintf;
32 using android::idmap2::utils::BitmaskToPolicies;
33 using android::idmap2::utils::IsReference;
34 using android::idmap2::utils::ResToTypeEntryName;
35 using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
36 using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
37 
38 namespace android::idmap2 {
39 
40 namespace {
41 
42 #define REWRITE_PACKAGE(resid, package_id) \
43   (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
44 #define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
45 
ConcatPolicies(const std::vector<std::string> & policies)46 std::string ConcatPolicies(const std::vector<std::string>& policies) {
47   std::string message;
48   for (const std::string& policy : policies) {
49     if (!message.empty()) {
50       message.append("|");
51     }
52     message.append(policy);
53   }
54 
55   return message;
56 }
57 
CheckOverlayable(const LoadedPackage & target_package,const OverlayManifestInfo & overlay_info,const PolicyBitmask & fulfilled_policies,const ResourceId & target_resource)58 Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
59                               const OverlayManifestInfo& overlay_info,
60                               const PolicyBitmask& fulfilled_policies,
61                               const ResourceId& target_resource) {
62   static constexpr const PolicyBitmask sDefaultPolicies =
63       PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
64       PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE;
65 
66   // If the resource does not have an overlayable definition, allow the resource to be overlaid if
67   // the overlay is preinstalled or signed with the same signature as the target.
68   if (!target_package.DefinesOverlayable()) {
69     return (sDefaultPolicies & fulfilled_policies) != 0
70                ? Result<Unit>({})
71                : Error(
72                      "overlay must be preinstalled or signed with the same signature as the "
73                      "target");
74   }
75 
76   const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
77   if (overlayable_info == nullptr) {
78     // Do not allow non-overlayable resources to be overlaid.
79     return Error("target resource has no overlayable declaration");
80   }
81 
82   if (overlay_info.target_name != overlayable_info->name) {
83     // If the overlay supplies a target overlayable name, the resource must belong to the
84     // overlayable defined with the specified name to be overlaid.
85     return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
86                  overlay_info.target_name.c_str(), overlayable_info->name.c_str());
87   }
88 
89   // Enforce policy restrictions if the resource is declared as overlayable.
90   if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
91     return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
92                  ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
93                  ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
94   }
95 
96   return Result<Unit>({});
97 }
98 
99 // TODO(martenkongstad): scan for package name instead of assuming package at index 0
100 //
101 // idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
102 // in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
103 // this assumption tends to work out. That said, the correct thing to do is to scan
104 // resources.arsc for a package with a given name as read from the package manifest instead of
105 // relying on a hard-coded index. This however requires storing the package name in the idmap
106 // header, which in turn requires incrementing the idmap version. Because the initial version of
107 // idmap2 is compatible with idmap, this will have to wait for now.
GetPackageAtIndex0(const LoadedArsc & loaded_arsc)108 const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
109   const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
110   if (packages.empty()) {
111     return nullptr;
112   }
113   int id = packages[0]->GetPackageId();
114   return loaded_arsc.GetPackageById(id);
115 }
116 
OpenNonAssetFromResource(const ResourceId & resource_id,const AssetManager2 & asset_manager)117 Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
118                                                         const AssetManager2& asset_manager) {
119   Res_value value{};
120   ResTable_config selected_config{};
121   uint32_t flags;
122   auto cookie =
123       asset_manager.GetResource(resource_id, /* may_be_bag */ false,
124                                 /* density_override */ 0U, &value, &selected_config, &flags);
125   if (cookie == kInvalidCookie) {
126     return Error("failed to find resource for id 0x%08x", resource_id);
127   }
128 
129   if (value.dataType != Res_value::TYPE_STRING) {
130     return Error("resource for is 0x%08x is not a file", resource_id);
131   }
132 
133   auto string_pool = asset_manager.GetStringPoolForCookie(cookie);
134   size_t len;
135   auto file_path16 = string_pool->stringAt(value.data, &len);
136   if (file_path16 == nullptr) {
137     return Error("failed to find string for index %d", value.data);
138   }
139 
140   // Load the overlay resource mappings from the file specified using android:resourcesMap.
141   auto file_path = String8(String16(file_path16));
142   auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
143   if (asset == nullptr) {
144     return Error("file \"%s\" not found", file_path.c_str());
145   }
146 
147   return asset;
148 }
149 
150 }  // namespace
151 
CreateResourceMapping(const AssetManager2 * target_am,const LoadedPackage * target_package,const LoadedPackage * overlay_package,size_t string_pool_offset,const XmlParser & overlay_parser,LogInfo & log_info)152 Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
153                                                                const LoadedPackage* target_package,
154                                                                const LoadedPackage* overlay_package,
155                                                                size_t string_pool_offset,
156                                                                const XmlParser& overlay_parser,
157                                                                LogInfo& log_info) {
158   ResourceMapping resource_mapping;
159   auto root_it = overlay_parser.tree_iterator();
160   if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
161     return Error("root element is not <overlay> tag");
162   }
163 
164   const uint8_t target_package_id = target_package->GetPackageId();
165   const uint8_t overlay_package_id = overlay_package->GetPackageId();
166   auto overlay_it_end = root_it.end();
167   for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
168     if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
169       return Error("failed to parse overlay xml document");
170     }
171 
172     if (overlay_it->event() != XmlParser::Event::START_TAG) {
173       continue;
174     }
175 
176     if (overlay_it->name() != "item") {
177       return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
178     }
179 
180     Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
181     if (!target_resource) {
182       return Error(R"(<item> tag missing expected attribute "target")");
183     }
184 
185     Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
186     if (!overlay_resource) {
187       return Error(R"(<item> tag missing expected attribute "value")");
188     }
189 
190     ResourceId target_id =
191         target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
192     if (target_id == 0U) {
193       log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
194                                     << "\" in target resources");
195       continue;
196     }
197 
198     // Retrieve the compile-time resource id of the target resource.
199     target_id = REWRITE_PACKAGE(target_id, target_package_id);
200 
201     if (overlay_resource->dataType == Res_value::TYPE_STRING) {
202       overlay_resource->data += string_pool_offset;
203     }
204 
205     // Only rewrite resources defined within the overlay package to their corresponding target
206     // resource ids at runtime.
207     bool rewrite_overlay_reference =
208         IsReference(overlay_resource->dataType)
209             ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
210             : false;
211 
212     if (rewrite_overlay_reference) {
213       overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
214     }
215 
216     resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
217                                 rewrite_overlay_reference);
218   }
219 
220   return resource_mapping;
221 }
222 
CreateResourceMappingLegacy(const AssetManager2 * target_am,const AssetManager2 * overlay_am,const LoadedPackage * target_package,const LoadedPackage * overlay_package)223 Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
224     const AssetManager2* target_am, const AssetManager2* overlay_am,
225     const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
226   ResourceMapping resource_mapping;
227   const uint8_t target_package_id = target_package->GetPackageId();
228   const auto end = overlay_package->end();
229   for (auto iter = overlay_package->begin(); iter != end; ++iter) {
230     const ResourceId overlay_resid = *iter;
231     Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
232     if (!name) {
233       continue;
234     }
235 
236     // Find the resource with the same type and entry name within the target package.
237     const std::string full_name =
238         base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
239     ResourceId target_resource = target_am->GetResourceId(full_name);
240     if (target_resource == 0U) {
241       continue;
242     }
243 
244     // Retrieve the compile-time resource id of the target resource.
245     target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
246 
247     resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
248                                 /* rewrite_overlay_reference */ false);
249   }
250 
251   return resource_mapping;
252 }
253 
FilterOverlayableResources(const AssetManager2 * target_am,const LoadedPackage * target_package,const LoadedPackage * overlay_package,const OverlayManifestInfo & overlay_info,const PolicyBitmask & fulfilled_policies,LogInfo & log_info)254 void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
255                                                  const LoadedPackage* target_package,
256                                                  const LoadedPackage* overlay_package,
257                                                  const OverlayManifestInfo& overlay_info,
258                                                  const PolicyBitmask& fulfilled_policies,
259                                                  LogInfo& log_info) {
260   std::set<ResourceId> remove_ids;
261   for (const auto& target_map : target_map_) {
262     const ResourceId target_resid = target_map.first;
263     Result<Unit> success =
264         CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
265     if (success) {
266       continue;
267     }
268 
269     // Attempting to overlay a resource that is not allowed to be overlaid is treated as a
270     // warning.
271     Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
272     if (!name) {
273       name = StringPrintf("0x%08x", target_resid);
274     }
275 
276     log_info.Warning(LogMessage() << "overlay \"" << overlay_package->GetPackageName()
277                                   << "\" is not allowed to overlay resource \"" << *name
278                                   << "\" in target: " << success.GetErrorMessage());
279 
280     remove_ids.insert(target_resid);
281   }
282 
283   for (const ResourceId target_resid : remove_ids) {
284     RemoveMapping(target_resid);
285   }
286 }
287 
FromApkAssets(const ApkAssets & target_apk_assets,const ApkAssets & overlay_apk_assets,const OverlayManifestInfo & overlay_info,const PolicyBitmask & fulfilled_policies,bool enforce_overlayable,LogInfo & log_info)288 Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
289                                                        const ApkAssets& overlay_apk_assets,
290                                                        const OverlayManifestInfo& overlay_info,
291                                                        const PolicyBitmask& fulfilled_policies,
292                                                        bool enforce_overlayable,
293                                                        LogInfo& log_info) {
294   AssetManager2 target_asset_manager;
295   if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
296                                          false /* filter_incompatible_configs*/)) {
297     return Error("failed to create target asset manager");
298   }
299 
300   AssetManager2 overlay_asset_manager;
301   if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true /* invalidate_caches */,
302                                           false /* filter_incompatible_configs */)) {
303     return Error("failed to create overlay asset manager");
304   }
305 
306   const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
307   if (target_arsc == nullptr) {
308     return Error("failed to load target resources.arsc");
309   }
310 
311   const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
312   if (overlay_arsc == nullptr) {
313     return Error("failed to load overlay resources.arsc");
314   }
315 
316   const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
317   if (target_pkg == nullptr) {
318     return Error("failed to load target package from resources.arsc");
319   }
320 
321   const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
322   if (overlay_pkg == nullptr) {
323     return Error("failed to load overlay package from resources.arsc");
324   }
325 
326   size_t string_pool_data_length = 0U;
327   size_t string_pool_offset = 0U;
328   std::unique_ptr<uint8_t[]> string_pool_data;
329   Result<ResourceMapping> resource_mapping = {{}};
330   if (overlay_info.resource_mapping != 0U) {
331     // Use the dynamic reference table to find the assigned resource id of the map xml.
332     const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
333     uint32_t resource_mapping_id = overlay_info.resource_mapping;
334     ref_table->lookupResourceId(&resource_mapping_id);
335 
336     // Load the overlay resource mappings from the file specified using android:resourcesMap.
337     auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager);
338     if (!asset) {
339       return Error("failed opening xml for android:resourcesMap: %s",
340                    asset.GetErrorMessage().c_str());
341     }
342 
343     auto parser =
344         XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
345     if (!parser) {
346       return Error("failed opening ResXMLTree");
347     }
348 
349     // Copy the xml string pool data before the parse goes out of scope.
350     auto& string_pool = (*parser)->get_strings();
351     string_pool_data_length = string_pool.bytes();
352     string_pool_data.reset(new uint8_t[string_pool_data_length]);
353     memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length);
354 
355     // Offset string indices by the size of the overlay resource table string pool.
356     string_pool_offset = overlay_arsc->GetStringPool()->size();
357 
358     resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
359                                              string_pool_offset, *(*parser), log_info);
360   } else {
361     // If no file is specified using android:resourcesMap, it is assumed that the overlay only
362     // defines resources intended to override target resources of the same type and name.
363     resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
364                                                    target_pkg, overlay_pkg);
365   }
366 
367   if (!resource_mapping) {
368     return resource_mapping.GetError();
369   }
370 
371   if (enforce_overlayable) {
372     // Filter out resources the overlay is not allowed to override.
373     (*resource_mapping)
374         .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
375                                     fulfilled_policies, log_info);
376   }
377 
378   resource_mapping->target_package_id_ = target_pkg->GetPackageId();
379   resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
380   resource_mapping->string_pool_offset_ = string_pool_offset;
381   resource_mapping->string_pool_data_ = std::move(string_pool_data);
382   resource_mapping->string_pool_data_length_ = string_pool_data_length;
383   return std::move(*resource_mapping);
384 }
385 
GetOverlayToTargetMap() const386 OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
387   // An overlay resource can override multiple target resources at once. Rewrite the overlay
388   // resource as the first target resource it overrides.
389   OverlayResourceMap map;
390   for (const auto& mappings : overlay_map_) {
391     map.insert(std::make_pair(mappings.first, mappings.second));
392   }
393   return map;
394 }
395 
AddMapping(ResourceId target_resource,TargetValue::DataType data_type,TargetValue::DataValue data_value,bool rewrite_overlay_reference)396 Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
397                                          TargetValue::DataType data_type,
398                                          TargetValue::DataValue data_value,
399                                          bool rewrite_overlay_reference) {
400   if (target_map_.find(target_resource) != target_map_.end()) {
401     return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
402   }
403 
404   // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
405   // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
406 
407   target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
408 
409   if (rewrite_overlay_reference && IsReference(data_type)) {
410     overlay_map_.insert(std::make_pair(data_value, target_resource));
411   }
412 
413   return Result<Unit>({});
414 }
415 
RemoveMapping(ResourceId target_resource)416 void ResourceMapping::RemoveMapping(ResourceId target_resource) {
417   auto target_iter = target_map_.find(target_resource);
418   if (target_iter == target_map_.end()) {
419     return;
420   }
421 
422   const TargetValue value = target_iter->second;
423   target_map_.erase(target_iter);
424 
425   if (!IsReference(value.data_type)) {
426     return;
427   }
428 
429   auto overlay_iter = overlay_map_.equal_range(value.data_value);
430   for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
431     if (i->second == target_resource) {
432       overlay_map_.erase(i);
433       return;
434     }
435   }
436 }
437 
438 }  // namespace android::idmap2
439