diff options
author | Ryan Mitchell <rtmitchell@google.com> | 2021-05-11 12:21:29 -0700 |
---|---|---|
committer | Ryan Mitchell <rtmitchell@google.com> | 2021-05-27 09:50:36 -0700 |
commit | c0416698dbaaeba7b706c9eca59e2ba0cab45377 (patch) | |
tree | 6c9b232eb3f6f52d7ab91c0f412688021a94b274 | |
parent | bef47a35776cf1d5ae9124b3a3823d1b950d0b80 (diff) |
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
-rw-r--r-- | cmds/idmap2/libidmap2/ResourceContainer.cpp | 2 | ||||
-rw-r--r-- | cmds/idmap2/tests/XmlParserTests.cpp | 2 | ||||
-rw-r--r-- | core/java/android/app/LoadedApk.java | 4 | ||||
-rw-r--r-- | core/java/android/app/ResourcesManager.java | 51 | ||||
-rw-r--r-- | core/java/android/content/res/ApkAssets.java | 6 | ||||
-rw-r--r-- | core/jni/android_content_res_ApkAssets.cpp | 37 | ||||
-rwxr-xr-x | libs/androidfw/ApkAssets.cpp | 4 | ||||
-rw-r--r-- | libs/androidfw/AssetsProvider.cpp | 16 | ||||
-rw-r--r-- | libs/androidfw/TEST_MAPPING | 3 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/AssetsProvider.h | 9 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/LoadedArsc.h | 4 |
11 files changed, 106 insertions, 32 deletions
diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp index 9147ccaaa17a..c147f6a6024b 100644 --- a/cmds/idmap2/libidmap2/ResourceContainer.cpp +++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp @@ -323,7 +323,7 @@ ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zi 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 eaf10a7d9282..016c5c3a01a5 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 0733a3e7c51c..90a6f32b2344 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1289,6 +1289,10 @@ public final class LoadedApk { 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 792336d9be9e..f28c760d54d9 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -39,6 +39,7 @@ import android.os.IBinder; 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 @@ public class ResourcesManager { */ 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 @@ public class ResourcesManager { } } + /** + * 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 @@ public class ResourcesManager { } } - // 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 @@ public class ResourcesManager { 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 224ff14c5df4..6fd2d05ad135 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -69,6 +69,12 @@ public final class ApkAssets { */ 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 9c8daec7919a..1be84282f6db 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -212,10 +212,11 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma 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 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t 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 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_ 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 26d836328b54..2beb33abe782 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -40,7 +40,7 @@ ApkAssets::ApkAssets(std::unique_ptr<Asset> resources_asset, 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 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, 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 6c7a25307247..bce34d37c90b 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -85,12 +85,14 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const { } 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 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) { 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 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd, 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 @@ std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path, 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 @@ std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path, } 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 766714cd7694..8a5c06d1eef9 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 ec51c65053bf..966ec74c1786 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -80,9 +80,12 @@ struct AssetsProvider { // 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 @@ struct ZipAssetsProvider : public AssetsProvider { 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 @@ struct ZipAssetsProvider : public AssetsProvider { 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 9bbdede56293..b3d6a4dcb955 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -92,6 +92,10 @@ enum : package_property_t { // 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 { |