diff options
| author | 2024-11-25 15:34:04 +0000 | |
|---|---|---|
| committer | 2024-11-25 20:36:08 +0000 | |
| commit | 1ac8ea647ee5bd9bf73223b2945b4311f83a7bed (patch) | |
| tree | 1ba2991f18a2d1cc505195e24ed548dc0a2261f6 | |
| parent | ea289d63ce310f09894d830f86c5e027b593d8c3 (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
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. |