summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Pawan Wagh <waghpawan@google.com> 2024-11-25 15:34:04 +0000
committer Pawan Wagh <waghpawan@google.com> 2024-11-25 20:36:08 +0000
commit1ac8ea647ee5bd9bf73223b2945b4311f83a7bed (patch)
tree1ba2991f18a2d1cc505195e24ed548dc0a2261f6
parentea289d63ce310f09894d830f86c5e027b593d8c3 (diff)
Reland "Implement alignment checks in PM"
This reverts commit 8a34831ed7ad51ea29efaee6dabf013d76c75336. Reason for revert: scan was failing when invalid value being set to page size compat mode flags Test: atest -c CtsPackageManagerHostTestCases Bug: 371049373 Change-Id: I9f8504ec2ba69b67740abe18dc1e643642b9bebb
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java53
-rw-r--r--core/jni/com_android_internal_content_NativeLibraryHelper.cpp210
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelper.java20
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java19
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java37
5 files changed, 311 insertions, 28 deletions
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 0c56c677e06f..6ad7fef95357 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -26,6 +26,7 @@ import static android.system.OsConstants.S_IXGRP;
import static android.system.OsConstants.S_IXOTH;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
@@ -177,6 +178,13 @@ public class NativeLibraryHelper {
private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
String abiToCopy, boolean extractNativeLibs, boolean debuggable);
+ private static native int nativeCheckAlignment(
+ long handle,
+ String sharedLibraryPath,
+ String abi,
+ boolean extractNativeLibs,
+ boolean debuggable);
+
private static long sumNativeBinaries(Handle handle, String abi) {
long sum = 0;
for (long apkHandle : handle.apkHandles) {
@@ -432,6 +440,51 @@ public class NativeLibraryHelper {
}
}
+ /**
+ * Checks alignment of APK and native libraries for 16KB device
+ *
+ * @param handle APK file to scan for native libraries
+ * @param libraryRoot directory for libraries
+ * @param abiOverride abiOverride for package
+ * @return {@link Modes from ApplicationInfo.PageSizeAppCompat} if successful or error code
+ * which suggests undefined mode
+ */
+ @ApplicationInfo.PageSizeAppCompatFlags
+ public static int checkAlignmentForCompatMode(
+ Handle handle,
+ String libraryRoot,
+ boolean nativeLibraryRootRequiresIsa,
+ String abiOverride) {
+ // Keep the code below in sync with copyNativeBinariesForSupportedAbi
+ int abi = findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
+ if (abi < 0) {
+ return ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ final String supportedAbi = Build.SUPPORTED_64_BIT_ABIS[abi];
+ final String instructionSet = VMRuntime.getInstructionSet(supportedAbi);
+ String subDir = libraryRoot;
+ if (nativeLibraryRootRequiresIsa) {
+ subDir += "/" + instructionSet;
+ }
+
+ int mode = ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ for (long apkHandle : handle.apkHandles) {
+ int res =
+ nativeCheckAlignment(
+ apkHandle,
+ subDir,
+ Build.SUPPORTED_64_BIT_ABIS[abi],
+ handle.extractNativeLibs,
+ handle.debuggable);
+ if (res == ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR) {
+ return res;
+ }
+ mode |= res;
+ }
+ return mode;
+ }
+
public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
throws IOException {
long sum = 0;
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 7ad18b83f0d6..b2eeff36c007 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -21,6 +21,7 @@
#include <androidfw/ApkParsing.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
+#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -38,6 +39,7 @@
#include <memory>
#include <string>
+#include <vector>
#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
@@ -60,6 +62,12 @@ enum install_status_t {
NO_NATIVE_LIBRARIES = -114
};
+// These code should match with PageSizeAppCompatFlags inside ApplicationInfo.java
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_ERROR = -1;
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED = 0;
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED = 1 << 1;
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED = 1 << 2;
+
typedef install_status_t (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*);
static bool
@@ -524,11 +532,7 @@ static inline bool app_compat_16kb_enabled() {
static const size_t kPageSize = getpagesize();
// App compat is only applicable on 16kb-page-size devices.
- if (kPageSize != 0x4000) {
- return false;
- }
-
- return android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false);
+ return kPageSize == 0x4000;
}
static jint
@@ -626,6 +630,166 @@ com_android_internal_content_NativeLibraryHelper_openApkFd(JNIEnv *env, jclass,
return reinterpret_cast<jlong>(zipFile);
}
+static jint checkLoadSegmentAlignment(const char* fileName, off64_t offset) {
+ std::vector<Elf64_Phdr> programHeaders;
+ if (!getLoadSegmentPhdrs(fileName, offset, programHeaders)) {
+ ALOGE("Failed to read program headers from ELF file.");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ int mode = PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ for (auto programHeader : programHeaders) {
+ if (programHeader.p_type != PT_LOAD) {
+ continue;
+ }
+
+ // Set ELF alignment bit if 4 KB aligned LOAD segment is found
+ if (programHeader.p_align == 0x1000) {
+ ALOGI("Setting page size compat mode PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED");
+ mode |= PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED;
+ break;
+ }
+ }
+
+ return mode;
+}
+
+static jint checkExtractedLibAlignment(ZipFileRO* zipFile, ZipEntryRO zipEntry,
+ const char* fileName, const std::string nativeLibPath) {
+ // Build local file path
+ const size_t fileNameLen = strlen(fileName);
+ char localFileName[nativeLibPath.size() + fileNameLen + 2];
+
+ if (strlcpy(localFileName, nativeLibPath.c_str(), sizeof(localFileName)) !=
+ nativeLibPath.size()) {
+ ALOGE("Couldn't allocate local file name for library");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ *(localFileName + nativeLibPath.size()) = '/';
+
+ if (strlcpy(localFileName + nativeLibPath.size() + 1, fileName,
+ sizeof(localFileName) - nativeLibPath.size() - 1) != fileNameLen) {
+ ALOGE("Couldn't allocate local file name for library");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ struct statfs64 fsInfo;
+ int result = statfs64(localFileName, &fsInfo);
+ if (result < 0) {
+ ALOGE("Failed to stat file :%s", localFileName);
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ return checkLoadSegmentAlignment(localFileName, 0);
+}
+
+static jint checkAlignment(JNIEnv* env, jstring javaNativeLibPath, jboolean extractNativeLibs,
+ ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName) {
+ int mode = PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ // Need two separate install status for APK and ELF alignment
+ static const size_t kPageSize = getpagesize();
+ jint ret = INSTALL_SUCCEEDED;
+
+ ScopedUtfChars nativeLibPath(env, javaNativeLibPath);
+ if (extractNativeLibs) {
+ ALOGI("extractNativeLibs specified, checking for extracted lib %s", fileName);
+ return checkExtractedLibAlignment(zipFile, zipEntry, fileName, nativeLibPath.c_str());
+ }
+
+ uint16_t method;
+ off64_t offset;
+ if (!zipFile->getEntryInfo(zipEntry, &method, nullptr, nullptr, &offset, nullptr, nullptr,
+ nullptr)) {
+ ALOGE("Couldn't read zip entry info from zipFile %s", zipFile->getZipFileName());
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ // check if library is uncompressed and page-aligned
+ if (method != ZipFileRO::kCompressStored) {
+ ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+ fileName);
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ if (offset % kPageSize != 0) {
+ ALOGW("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
+ "from apk.\n",
+ fileName, kPageSize);
+ mode |= PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED;
+ ALOGI("Setting page size compat mode "
+ "PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED for %s",
+ zipFile->getZipFileName());
+ }
+
+ int loadMode = checkLoadSegmentAlignment(zipFile->getZipFileName(), offset);
+ if (loadMode == PAGE_SIZE_APP_COMPAT_FLAG_ERROR) {
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ mode |= loadMode;
+ return mode;
+}
+
+// TODO(b/371049373): This function is copy of iterateOverNativeFiles with different way of handling
+// and combining return values for all ELF and APKs. Find a way to consolidate two functions.
+static jint com_android_internal_content_NativeLibraryHelper_checkApkAlignment(
+ JNIEnv* env, jclass clazz, jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
+ jboolean extractNativeLibs, jboolean debuggable) {
+ int mode = PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+ if (zipFile == nullptr) {
+ ALOGE("zipfile handle is null");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ auto result = NativeLibrariesIterator::create(zipFile, debuggable);
+ if (!result.ok()) {
+ ALOGE("Can't iterate over native libs for file:%s", zipFile->getZipFileName());
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ std::unique_ptr<NativeLibrariesIterator> it(std::move(result.value()));
+
+ const ScopedUtfChars cpuAbi(env, javaCpuAbi);
+ if (cpuAbi.c_str() == nullptr) {
+ ALOGE("cpuAbi is nullptr");
+ // This would've thrown, so this return code isn't observable by Java.
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ while (true) {
+ auto next = it->next();
+ if (!next.ok()) {
+ ALOGE("next iterator not found Error:%d", next.error());
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ auto entry = next.value();
+ if (entry == nullptr) {
+ break;
+ }
+
+ const char* fileName = it->currentEntry();
+ const char* lastSlash = it->lastSlash();
+
+ // Check to make sure the CPU ABI of this file is one we support.
+ const char* cpuAbiOffset = fileName + APK_LIB_LEN;
+ const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset;
+
+ if (cpuAbi.size() == cpuAbiRegionSize &&
+ !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
+ int ret = checkAlignment(env, javaNativeLibPath, extractNativeLibs, zipFile, entry,
+ lastSlash + 1);
+ if (ret == PAGE_SIZE_APP_COMPAT_FLAG_ERROR) {
+ ALOGE("Alignment check returned for zipfile: %s, entry:%s",
+ zipFile->getZipFileName(), lastSlash + 1);
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ mode |= ret;
+ }
+ }
+
+ return mode;
+}
+
static void
com_android_internal_content_NativeLibraryHelper_close(JNIEnv *env, jclass, jlong apkHandle)
{
@@ -633,29 +797,23 @@ com_android_internal_content_NativeLibraryHelper_close(JNIEnv *env, jclass, jlon
}
static const JNINativeMethod gMethods[] = {
- {"nativeOpenApk",
- "(Ljava/lang/String;)J",
- (void *)com_android_internal_content_NativeLibraryHelper_openApk},
- {"nativeOpenApkFd",
- "(Ljava/io/FileDescriptor;Ljava/lang/String;)J",
- (void *)com_android_internal_content_NativeLibraryHelper_openApkFd},
- {"nativeClose",
- "(J)V",
- (void *)com_android_internal_content_NativeLibraryHelper_close},
- {"nativeCopyNativeBinaries",
- "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
- (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
- {"nativeSumNativeBinaries",
- "(JLjava/lang/String;Z)J",
- (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
- {"nativeFindSupportedAbi",
- "(J[Ljava/lang/String;Z)I",
- (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
- {"hasRenderscriptBitcode", "(J)I",
- (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
+ {"nativeOpenApk", "(Ljava/lang/String;)J",
+ (void*)com_android_internal_content_NativeLibraryHelper_openApk},
+ {"nativeOpenApkFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;)J",
+ (void*)com_android_internal_content_NativeLibraryHelper_openApkFd},
+ {"nativeClose", "(J)V", (void*)com_android_internal_content_NativeLibraryHelper_close},
+ {"nativeCopyNativeBinaries", "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
+ {"nativeSumNativeBinaries", "(JLjava/lang/String;Z)J",
+ (void*)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
+ {"nativeFindSupportedAbi", "(J[Ljava/lang/String;Z)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
+ {"hasRenderscriptBitcode", "(J)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
+ {"nativeCheckAlignment", "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_checkApkAlignment},
};
-
int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env)
{
return RegisterMethodsOrDie(env,
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index c66a9e98c1d3..09302996d228 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.util.ArraySet;
import android.util.Pair;
@@ -28,8 +29,6 @@ import com.android.server.pm.pkg.PackageStateInternal;
import java.io.File;
-
-
// TODO: Move to .parsing sub-package
@VisibleForTesting
public interface PackageAbiHelper {
@@ -79,6 +78,23 @@ public interface PackageAbiHelper {
AndroidPackage scannedPackage);
/**
+ * Checks alignment of APK and native libraries for 16KB device
+ *
+ * @param pkg AndroidPackage for which alignment check is being done
+ * @param libraryRoot directory for libraries
+ * @param nativeLibraryRootRequiresIsa use isa
+ * @param cpuAbiOverride ABI override mentioned in package
+ * @return {ApplicationInfo.PageSizeAppCompat} if successful or error code
+ * which suggests undefined mode
+ */
+ @ApplicationInfo.PageSizeAppCompatFlags
+ int checkPackageAlignment(
+ AndroidPackage pkg,
+ String libraryRoot,
+ boolean nativeLibraryRootRequiresIsa,
+ String cpuAbiOverride);
+
+ /**
* The native library paths and related properties that should be set on a
* {@link ParsedPackage}.
*/
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 9db4d33106ce..7229f070acf0 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -29,6 +29,7 @@ import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.os.Build;
@@ -625,4 +626,22 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
}
return adjustedAbi;
}
+
+ @Override
+ public int checkPackageAlignment(
+ AndroidPackage pkg,
+ String libraryRoot,
+ boolean nativeLibraryRootRequiresIsa,
+ String abiOverride) {
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = AndroidPackageUtils.createNativeLibraryHandle(pkg);
+ return NativeLibraryHelper.checkAlignmentForCompatMode(
+ handle, libraryRoot, nativeLibraryRootRequiresIsa, abiOverride);
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Failed to check alignment of package : "
+ + pkg.getPackageName());
+ return ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 0802e9ee50bc..a317e1628f97 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -52,6 +52,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTi
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
@@ -63,6 +64,8 @@ import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.system.Os;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -105,6 +108,9 @@ import java.util.UUID;
* Helper class that handles package scanning logic
*/
final class ScanPackageUtils {
+
+ public static final int PAGE_SIZE_16KB = 16384;
+
/**
* Just scans the package without any side effects.
*
@@ -418,6 +424,37 @@ final class ScanPackageUtils {
+ " abiOverride=" + pkgSetting.getCpuAbiOverride());
}
+ boolean is16KbDevice = Os.sysconf(OsConstants._SC_PAGESIZE) == PAGE_SIZE_16KB;
+ if (Flags.appCompatOption16kb() && is16KbDevice) {
+ // Alignment checks are used decide whether this app should run in compat mode when
+ // nothing was specified in manifest. Manifest should always take precedence over
+ // something decided by platform.
+ if (parsedPackage.getPageSizeAppCompatFlags()
+ > ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED) {
+ pkgSetting.setPageSizeAppCompatFlags(parsedPackage.getPageSizeAppCompatFlags());
+ } else {
+ // 16 KB is only support for 64 bit ABIs and for apps which are being installed
+ // Check alignment. System, Apex and Platform packages should be page-agnostic now
+ if ((Build.SUPPORTED_64_BIT_ABIS.length > 0)
+ && !isSystemApp
+ && !isApex
+ && !isPlatformPackage) {
+ int mode =
+ packageAbiHelper.checkPackageAlignment(
+ parsedPackage,
+ pkgSetting.getLegacyNativeLibraryPath(),
+ parsedPackage.isNativeLibraryRootRequiresIsa(),
+ pkgSetting.getCpuAbiOverride());
+ if (mode >= ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED) {
+ pkgSetting.setPageSizeAppCompatFlags(mode);
+ } else {
+ Slog.e(TAG, "Error occurred while checking alignment of package : "
+ + parsedPackage.getPackageName());
+ }
+ }
+ }
+ }
+
if ((scanFlags & SCAN_BOOTING) == 0 && oldSharedUserSetting != null) {
// We don't do this here during boot because we can do it all
// at once after scanning all existing packages.