Disable incremental hardening on own resources
When an application is incrementally installed, and a resources
operation fails due to the resources not being fully present,
the app should crash instead of swallowing the error and
returning default values to not alter the experience of
using the application.
Disable IncFsFileMap protections on ApkAssets that are a part of the
application that is running (base and splits).
Bug: 187220960
Test: atest ResourcesHardeningTest
Change-Id: Ibc67aca688720f983c7c656f404593285a54999b
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp
index 9147cca..c147f6a 100644
--- a/cmds/idmap2/libidmap2/ResourceContainer.cpp
+++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp
@@ -323,7 +323,7 @@
Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath(
const std::string& path) {
- auto zip_assets = ZipAssetsProvider::Create(path);
+ auto zip_assets = ZipAssetsProvider::Create(path, 0 /* flags */);
if (zip_assets == nullptr) {
return Error("failed to load zip assets");
}
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
index eaf10a7..016c5c3 100644
--- a/cmds/idmap2/tests/XmlParserTests.cpp
+++ b/cmds/idmap2/tests/XmlParserTests.cpp
@@ -26,7 +26,7 @@
namespace android::idmap2 {
Result<XmlParser> CreateTestParser(const std::string& test_file) {
- auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk");
+ auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk", 0 /* flags */);
if (zip == nullptr) {
return Error("Failed to open zip file");
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0733a3e..90a6f32 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1289,6 +1289,10 @@
throw new AssertionError("null split not found");
}
+ if (Process.myUid() == mApplicationInfo.uid) {
+ ResourcesManager.getInstance().initializeApplicationPaths(mResDir, splitPaths);
+ }
+
mResources = ResourcesManager.getInstance().getResources(null, mResDir,
splitPaths, mLegacyOverlayDirs, mOverlayPaths,
mApplicationInfo.sharedLibraryFiles, null, null, getCompatibilityInfo(),
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 792336d..f28c760 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -39,6 +39,7 @@
import android.os.Process;
import android.os.Trace;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -260,6 +261,12 @@
*/
private final UpdateHandler mUpdateCallbacks = new UpdateHandler();
+ /**
+ * The set of APK paths belonging to this process. This is used to disable incremental
+ * installation crash protections on these APKs so the app either behaves as expects or crashes.
+ */
+ private final ArraySet<String> mApplicationOwnedApks = new ArraySet<>();
+
@UnsupportedAppUsage
public ResourcesManager() {
}
@@ -424,6 +431,32 @@
}
}
+ /**
+ * Initializes the set of APKs owned by the application running in this process.
+ */
+ public void initializeApplicationPaths(@NonNull String sourceDir,
+ @Nullable String[] splitDirs) {
+ synchronized (mLock) {
+ if (mApplicationOwnedApks.isEmpty()) {
+ addApplicationPathsLocked(sourceDir, splitDirs);
+ }
+ }
+ }
+
+ /**
+ * Updates the set of APKs owned by the application running in this process.
+ *
+ * This method only appends to the set of APKs owned by this process because the previous APKs
+ * paths still belong to the application running in this process.
+ */
+ private void addApplicationPathsLocked(@NonNull String sourceDir,
+ @Nullable String[] splitDirs) {
+ mApplicationOwnedApks.add(sourceDir);
+ if (splitDirs != null) {
+ mApplicationOwnedApks.addAll(Arrays.asList(splitDirs));
+ }
+ }
+
private static String overlayPathToIdmapPath(String path) {
return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
}
@@ -445,13 +478,17 @@
}
}
- // We must load this from disk.
+ int flags = 0;
+ if (key.sharedLib) {
+ flags |= ApkAssets.PROPERTY_DYNAMIC;
+ }
+ if (mApplicationOwnedApks.contains(key.path)) {
+ flags |= ApkAssets.PROPERTY_DISABLE_INCREMENTAL_HARDENING;
+ }
if (key.overlay) {
- apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path),
- 0 /*flags*/);
+ apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(key.path), flags);
} else {
- apkAssets = ApkAssets.loadFromPath(key.path,
- key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
+ apkAssets = ApkAssets.loadFromPath(key.path, flags);
}
synchronized (mLock) {
@@ -1437,6 +1474,10 @@
String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
appInfo.overlayPaths);
+ if (appInfo.uid == myUid) {
+ addApplicationPathsLocked(baseCodePath, copiedSplitDirs);
+ }
+
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
final int implCount = mResourceImpls.size();
for (int i = 0; i < implCount; i++) {
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 224ff14c..6fd2d05 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -69,6 +69,12 @@
*/
private static final int PROPERTY_OVERLAY = 1 << 3;
+ /**
+ * The apk assets is owned by the application running in this process and incremental crash
+ * protections for this APK must be disabled.
+ */
+ public static final int PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1 << 4;
+
/** Flags that change the behavior of loaded apk assets. */
@IntDef(prefix = { "PROPERTY_" }, value = {
PROPERTY_SYSTEM,
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 9c8daec..1be8428 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -212,10 +212,11 @@
std::unique_ptr<ApkAssets> apk_assets;
switch (format) {
case FORMAT_APK: {
- auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
- ZipAssetsProvider::Create(path.c_str()));
- apk_assets = ApkAssets::Load(std::move(assets), property_flags);
- break;
+ auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+ ZipAssetsProvider::Create(path.c_str(),
+ property_flags));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
+ break;
}
case FORMAT_IDMAP:
apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
@@ -271,11 +272,13 @@
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
case FORMAT_APK: {
- auto assets = MultiAssetsProvider::Create(
- std::move(loader_assets),
- ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str()));
- apk_assets = ApkAssets::Load(std::move(assets), property_flags);
- break;
+ auto assets =
+ MultiAssetsProvider::Create(std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd),
+ friendly_name_utf8.c_str(),
+ property_flags));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
+ break;
}
case FORMAT_ARSC:
apk_assets = ApkAssets::LoadTable(
@@ -336,12 +339,16 @@
std::unique_ptr<const ApkAssets> apk_assets;
switch (format) {
case FORMAT_APK: {
- auto assets = MultiAssetsProvider::Create(
- std::move(loader_assets),
- ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str(),
- static_cast<off64_t>(offset), static_cast<off64_t>(length)));
- apk_assets = ApkAssets::Load(std::move(assets), property_flags);
- break;
+ auto assets =
+ MultiAssetsProvider::Create(std::move(loader_assets),
+ ZipAssetsProvider::Create(std::move(dup_fd),
+ friendly_name_utf8.c_str(),
+ property_flags,
+ static_cast<off64_t>(offset),
+ static_cast<off64_t>(
+ length)));
+ apk_assets = ApkAssets::Load(std::move(assets), property_flags);
+ break;
}
case FORMAT_ARSC:
apk_assets = ApkAssets::LoadTable(
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 26d8363..2beb33a 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -40,7 +40,7 @@
loaded_idmap_(std::move(loaded_idmap)) {}
std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path, package_property_t flags) {
- return Load(ZipAssetsProvider::Create(path), flags);
+ return Load(ZipAssetsProvider::Create(path, flags), flags);
}
std::unique_ptr<ApkAssets> ApkAssets::LoadFromFd(base::unique_fd fd,
@@ -91,7 +91,7 @@
overlay_assets = EmptyAssetsProvider::Create(overlay_path);
} else {
// The overlay should be an APK.
- overlay_assets = ZipAssetsProvider::Create(overlay_path);
+ overlay_assets = ZipAssetsProvider::Create(overlay_path, flags);
}
if (overlay_assets == nullptr) {
return {};
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 6c7a253..bce34d3 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -85,12 +85,14 @@
}
ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
- time_t last_mod_time)
+ package_property_t flags, time_t last_mod_time)
: zip_handle_(handle, ::CloseArchive),
name_(std::forward<PathOrDebugName>(path)),
+ flags_(flags),
last_mod_time_(last_mod_time) {}
-std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
+ package_property_t flags) {
ZipArchiveHandle handle;
if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result);
@@ -109,11 +111,12 @@
return std::unique_ptr<ZipAssetsProvider>(
new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
- true /* is_path */}, sb.st_mtime));
+ true /* is_path */}, flags, sb.st_mtime));
}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
std::string friendly_name,
+ package_property_t flags,
off64_t offset,
off64_t len) {
ZipArchiveHandle handle;
@@ -140,7 +143,7 @@
return std::unique_ptr<ZipAssetsProvider>(
new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
- false /* is_path */}, sb.st_mtime));
+ false /* is_path */}, flags, sb.st_mtime));
}
std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -161,10 +164,11 @@
const int fd = GetFileDescriptor(zip_handle_.get());
const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get());
+ const bool incremental_hardening = (flags_ & PROPERTY_DISABLE_INCREMENTAL_HARDENING) == 0U;
incfs::IncFsFileMap asset_map;
if (entry.method == kCompressDeflated) {
if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length,
- name_.GetDebugName().c_str())) {
+ name_.GetDebugName().c_str(), incremental_hardening)) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName()
<< "'";
return {};
@@ -181,7 +185,7 @@
}
if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length,
- name_.GetDebugName().c_str())) {
+ name_.GetDebugName().c_str(), incremental_hardening)) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
return {};
}
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index 766714c..8a5c06d 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
+ },
+ {
+ "name": "ResourcesHardeningTest"
}
]
}
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index ec51c65..966ec74 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -80,9 +80,12 @@
// Supplies assets from a zip archive.
struct ZipAssetsProvider : public AssetsProvider {
- static std::unique_ptr<ZipAssetsProvider> Create(std::string path);
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path,
+ package_property_t flags);
+
static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
std::string friendly_name,
+ package_property_t flags,
off64_t offset = 0,
off64_t len = kUnknownLength);
@@ -101,7 +104,8 @@
private:
struct PathOrDebugName;
- ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time);
+ ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, package_property_t flags,
+ time_t last_mod_time);
struct PathOrDebugName {
PathOrDebugName(std::string&& value, bool is_path);
@@ -119,6 +123,7 @@
std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
PathOrDebugName name_;
+ package_property_t flags_;
time_t last_mod_time_;
};
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 9bbdede..b3d6a4d 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -92,6 +92,10 @@
// The package is a RRO.
PROPERTY_OVERLAY = 1U << 3U,
+
+ // The apk assets is owned by the application running in this process and incremental crash
+ // protections for this APK must be disabled.
+ PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1U << 4U,
};
struct OverlayableInfo {