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
This commit is contained in:
Ryan Mitchell
2021-05-11 12:21:29 -07:00
parent bef47a3577
commit c0416698db
11 changed files with 106 additions and 32 deletions

View File

@@ -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");
}

View File

@@ -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");
}

View File

@@ -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(),

View File

@@ -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++) {

View File

@@ -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,

View File

@@ -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(

View File

@@ -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 {};

View File

@@ -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 {};
}

View File

@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
},
{
"name": "ResourcesHardeningTest"
}
]
}

View File

@@ -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_;
};

View File

@@ -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 {