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:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "CtsResourcesLoaderTests"
|
||||
},
|
||||
{
|
||||
"name": "ResourcesHardeningTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user