Enforce overlayable API when defined

If a package defines overlayable resources, then do not allow resources
that are not defined as overlayable to be overlaid.

Bug:123600120
Test: idmap2_tests and cts-tradefed run cts -m CtsRROTestCases
Change-Id: I35120a97ccf4650e67c7ba65a60f4f3c51b0e627
This commit is contained in:
Ryan Mitchell
2019-01-29 12:01:24 -08:00
parent f727b8cf97
commit 198234502b
22 changed files with 212 additions and 30 deletions

View File

@@ -16,11 +16,11 @@ cc_defaults {
name: "idmap2_defaults",
tidy: true,
tidy_checks: [
"modernize-*",
"-modernize-avoid-c-arrays",
"android-*",
"misc-*",
"modernize-*",
"readability-*",
"-modernize-avoid-c-arrays",
],
tidy_flags: [
"-system-headers",

View File

@@ -281,10 +281,10 @@ bool CheckOverlayable(const LoadedPackage& target_package,
if (overlayable_info == nullptr) {
// If the resource does not have an overlayable definition, allow the resource to be overlaid.
// Once overlayable enforcement is turned on, this check will return false.
return true;
return !target_package.DefinesOverlayable();
}
if (!overlay_info.target_name.empty() && overlay_info.target_name != overlayable_info->name) {
if (overlay_info.target_name != overlayable_info->name) {
// If the overlay supplies a target overlayable name, the resource must belong to the
// overlayable defined with the specified name to be overlaid.
return false;

View File

@@ -40,9 +40,14 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
ASSERT_THAT(v, NotNull());
ASSERT_EQ(v->size(), 6U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target",
root + "/system-overlay", root + "/system-overlay-invalid"}));
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), std::set<std::string>({
root + "/.",
root + "/..",
root + "/overlay",
root + "/target",
root + "/system-overlay",
root + "/system-overlay-invalid",
}));
}
TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
@@ -51,13 +56,15 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
});
ASSERT_THAT(v, NotNull());
ASSERT_EQ(v->size(), 6U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
root + "/overlay/overlay-static-1.apk",
root + "/overlay/overlay-static-2.apk",
root + "/system-overlay/system-overlay.apk",
root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
ASSERT_EQ(v->size(), 9U);
ASSERT_EQ(
std::set<std::string>(v->begin(), v->end()),
std::set<std::string>(
{root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-static-1.apk",
root + "/overlay/overlay-static-2.apk", root + "/system-overlay/system-overlay.apk",
root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
}
TEST(FileUtilsTests, ReadFile) {

View File

@@ -163,8 +163,12 @@ TEST_F(Idmap2BinaryTests, Dump) {
TEST_F(Idmap2BinaryTests, Scan) {
SKIP_TEST_IF_CANT_EXEC_IDMAP2;
const std::string overlay_static_no_name_apk_path =
GetTestDataPath() + "/overlay/overlay-no-name-static.apk";
const std::string overlay_static_1_apk_path = GetTestDataPath() + "/overlay/overlay-static-1.apk";
const std::string overlay_static_2_apk_path = GetTestDataPath() + "/overlay/overlay-static-2.apk";
const std::string idmap_static_no_name_path =
Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_no_name_apk_path);
const std::string idmap_static_1_path =
Idmap::CanonicalIdmapPathFor(GetTempDirPath(), overlay_static_1_apk_path);
const std::string idmap_static_2_path =
@@ -184,11 +188,18 @@ TEST_F(Idmap2BinaryTests, Scan) {
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
std::stringstream expected;
expected << idmap_static_no_name_path << std::endl;
expected << idmap_static_1_path << std::endl;
expected << idmap_static_2_path << std::endl;
ASSERT_EQ(result->stdout, expected.str());
std::stringstream error;
auto idmap_static_no_name_raw_string = utils::ReadFile(idmap_static_no_name_path);
auto idmap_static_no_name_raw_stream = std::istringstream(*idmap_static_no_name_raw_string);
auto idmap_static_no_name = Idmap::FromBinaryStream(idmap_static_no_name_raw_stream, error);
ASSERT_THAT(idmap_static_no_name, NotNull());
ASSERT_IDMAP(*idmap_static_no_name, GetTargetApkPath(), overlay_static_no_name_apk_path);
auto idmap_static_1_raw_string = utils::ReadFile(idmap_static_1_path);
auto idmap_static_1_raw_stream = std::istringstream(*idmap_static_1_raw_string);
auto idmap_static_1 = Idmap::FromBinaryStream(idmap_static_1_raw_stream, error);
@@ -201,6 +212,7 @@ TEST_F(Idmap2BinaryTests, Scan) {
ASSERT_THAT(idmap_static_2, NotNull());
ASSERT_IDMAP(*idmap_static_2, GetTargetApkPath(), overlay_static_2_apk_path);
unlink(idmap_static_no_name_path.c_str());
unlink(idmap_static_2_path.c_str());
unlink(idmap_static_1_path.c_str());
@@ -218,6 +230,7 @@ TEST_F(Idmap2BinaryTests, Scan) {
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
ASSERT_EQ(result->stdout, expected.str());
unlink(idmap_static_no_name_path.c_str());
unlink(idmap_static_2_path.c_str());
unlink(idmap_static_1_path.c_str());
@@ -236,6 +249,7 @@ TEST_F(Idmap2BinaryTests, Scan) {
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
ASSERT_EQ(result->stdout, expected.str());
unlink(idmap_static_no_name_path.c_str());
unlink(idmap_static_2_path.c_str());
unlink(idmap_static_1_path.c_str());

View File

@@ -224,7 +224,8 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) {
ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
}
TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
@@ -260,7 +261,8 @@ TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_system_vendor
}
TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
@@ -290,17 +292,15 @@ TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
ASSERT_EQ(types[0]->GetEntryCount(), 6U);
ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/other
ASSERT_EQ(types[0]->GetEntry(2), kNoEntry); // string/policy_product
ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_public
ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system
ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system_vendor
ASSERT_EQ(types[0]->GetEntryCount(), 3U);
ASSERT_EQ(types[0]->GetEntryOffset(), 6U);
ASSERT_EQ(types[0]->GetEntry(0), 0x0003U); // string/policy_public
ASSERT_EQ(types[0]->GetEntry(1), 0x0004U); // string/policy_system
ASSERT_EQ(types[0]->GetEntry(2), 0x0005U); // string/policy_system_vendor
}
TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
// Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled.
TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
@@ -340,6 +340,91 @@ TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlaya
ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system_vendor
}
// The resources of APKs that do not include an overlayable declaration should not restrict what
// resources can be overlaid.
TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayable) {
const std::string target_apk_path(GetTestDataPath() + "/target/target-no-overlayable.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
const std::string overlay_apk_path(GetTestDataPath() +
"/system-overlay-invalid/system-overlay-invalid.apk");
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
ASSERT_EQ(types.size(), 1U);
ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
ASSERT_EQ(types[0]->GetEntryCount(), 6U);
ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/other
ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_product
ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_public
ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system
ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_system_vendor
}
// The resources of APKs that do not include an overlayable declaration should not restrict what
// resources can be overlaid.
TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
const std::string target_apk_path(GetTestDataPath() + "/target/target-no-overlayable.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay-no-name.apk");
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
ASSERT_EQ(types.size(), 2U);
ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
ASSERT_EQ(types[0]->GetEntryCount(), 1U);
ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
ASSERT_EQ(types[1]->GetEntryCount(), 4U);
ASSERT_EQ(types[1]->GetEntryOffset(), 9U);
ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
}
TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
std::string target_apk_path(GetTestDataPath());
for (int i = 0; i < 32; i++) {

View File

@@ -17,5 +17,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay">
<overlay
android:targetPackage="test.target" />
android:targetPackage="test.target"
android:targetName="TestResources"/>
</manifest>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay.no.name">
<overlay
android:targetPackage="test.target"/>
</manifest>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay.no.name.static">
<overlay
android:targetPackage="test.target"
android:targetName="TestResources"
android:isStatic="true"
android:priority="1" />
</manifest>

View File

@@ -18,6 +18,7 @@
package="test.overlay.static1">
<overlay
android:targetPackage="test.target"
android:targetName="TestResources"
android:isStatic="true"
android:priority="1" />
</manifest>

View File

@@ -18,6 +18,7 @@
package="test.overlay.static2">
<overlay
android:targetPackage="test.target"
android:targetName="TestResources"
android:isStatic="true"
android:priority="2" />
</manifest>

View File

@@ -23,6 +23,20 @@ aapt2 link \
-o overlay.apk \
compiled.flata
aapt2 link \
--no-resource-removal \
-I "$FRAMEWORK_RES_APK" \
--manifest AndroidManifestNoName.xml \
-o overlay-no-name.apk \
compiled.flata
aapt2 link \
--no-resource-removal \
-I "$FRAMEWORK_RES_APK" \
--manifest AndroidManifestNoNameStatic.xml \
-o overlay-no-name-static.apk \
compiled.flata
aapt2 link \
--no-resource-removal \
-I "$FRAMEWORK_RES_APK" \

Binary file not shown.

View File

@@ -14,8 +14,9 @@
limitations under the License.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay.system">
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay.system">
<overlay
android:targetPackage="test.target" />
android:targetPackage="test.target"
android:targetName="TestResources"/>
</manifest>

View File

@@ -15,3 +15,7 @@
aapt2 compile --dir res -o compiled.flata
aapt2 link --manifest AndroidManifest.xml -A assets -o target.apk compiled.flata
rm compiled.flata
aapt2 compile res/values/values.xml -o .
aapt2 link --manifest AndroidManifest.xml -A assets -o target_no_overlayable.apk values_values.arsc.flat
rm values_values.arsc.flat

View File

@@ -633,6 +633,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
overlayable_info.actor = actor;
overlayable_info.policy_flags = policy_header->policy_flags;
loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
loaded_package->defines_overlayable_ = true;
break;
}

View File

@@ -223,7 +223,7 @@ class LoadedPackage {
}
}
// Retrieve the overlayable properties of the specified resource. If the resource is not
// Retrieves the overlayable properties of the specified resource. If the resource is not
// overlayable, this will return a null pointer.
const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const {
for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids
@@ -235,6 +235,13 @@ class LoadedPackage {
return nullptr;
}
// Retrieves whether or not the package defines overlayable resources.
// TODO(123905379): Remove this when the enforcement of overlayable is turned on for all APK and
// not just those that defined overlayable resources.
bool DefinesOverlayable() const {
return defines_overlayable_;
}
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
@@ -248,6 +255,7 @@ class LoadedPackage {
bool dynamic_ = false;
bool system_ = false;
bool overlay_ = false;
bool defines_overlayable_ = false;
ByteBucketArray<TypeSpecPtr> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;