From fe85ed2ef53a7b1442a0e87f47e23e407e3e2b8c Mon Sep 17 00:00:00 2001 From: Evgenii Stepanov Date: Mon, 10 Jan 2022 12:25:40 -0800 Subject: [PATCH] Fix memory flags in external services and secondary zygotes. This change fixes a number of interconnected issues in memory safety (MTE, GWP-ASan, nativeHeapZeroInit) runtime flags. * Exported services use the hosting app UID to locate the process definition, and fail 100% of the time. Use the defining app UID and package name instead. * Isolated services process name does not match the name in the defining app manifest, because it includes a class name and an instance number. Pass the defining process name in HostingRecord to address this. * Exported service ApplicationInfo.packageName refers to the hosting app, again. As a result, wrong compat feature overrides are applied. This has been fixed before for AppZygote services; extend the fix to all external services. * Pass correct memory runtimeFlags to WebViewZygote. This is important because both MTE and GWP-ASan have a one-way disable switch; they are enabled in the Zygote and disabled in the apps that do not opt-in. Passing 0 runtimeFlags to WebViewZygote (and AppZygote) makes it impossible to enable these features later in their child processes. This change moves runtimeFlags logic from ProcessList to os.Zygote to make it available to WebViewZygote. Bug: 208910418 Test: CtsTaggingHostTestCases Test: atest in frameworks/base Test: CtsWebkitTestCases Test: manual install WebView with android:memtagMode tag Change-Id: I232d35344f4cd34226ff11324421904b35251525 --- core/java/android/os/AppZygote.java | 15 +- core/java/android/webkit/WebViewZygote.java | 5 +- core/java/com/android/internal/os/Zygote.java | 263 +++++++++++++++++- core/jni/com_android_internal_os_Zygote.cpp | 6 +- .../com/android/server/am/ActiveServices.java | 8 +- .../com/android/server/am/HostingRecord.java | 38 ++- .../com/android/server/am/ProcessList.java | 220 ++------------- .../com/android/server/am/ProcessRecord.java | 35 ++- .../server/am/ApplicationExitInfoTest.java | 2 +- 9 files changed, 349 insertions(+), 243 deletions(-) diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java index c8b4226ecae0f..07fbe4a04ff18 100644 --- a/core/java/android/os/AppZygote.java +++ b/core/java/android/os/AppZygote.java @@ -17,9 +17,11 @@ package android.os; import android.content.pm.ApplicationInfo; +import android.content.pm.ProcessInfo; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.Zygote; import dalvik.system.VMRuntime; @@ -45,8 +47,6 @@ public class AppZygote { // Last UID/GID of the range the AppZygote can setuid()/setgid() to private final int mZygoteUidGidMax; - private final int mZygoteRuntimeFlags; - private final Object mLock = new Object(); /** @@ -57,14 +57,15 @@ public class AppZygote { private ChildZygoteProcess mZygote; private final ApplicationInfo mAppInfo; + private final ProcessInfo mProcessInfo; - public AppZygote(ApplicationInfo appInfo, int zygoteUid, int uidGidMin, int uidGidMax, - int runtimeFlags) { + public AppZygote(ApplicationInfo appInfo, ProcessInfo processInfo, int zygoteUid, int uidGidMin, + int uidGidMax) { mAppInfo = appInfo; + mProcessInfo = processInfo; mZygoteUid = zygoteUid; mZygoteUidGidMin = uidGidMin; mZygoteUidGidMax = uidGidMax; - mZygoteRuntimeFlags = runtimeFlags; } /** @@ -108,13 +109,15 @@ public class AppZygote { String abi = mAppInfo.primaryCpuAbi != null ? mAppInfo.primaryCpuAbi : Build.SUPPORTED_ABIS[0]; try { + int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote( + mAppInfo, mProcessInfo); mZygote = Process.ZYGOTE_PROCESS.startChildZygote( "com.android.internal.os.AppZygoteInit", mAppInfo.processName + "_zygote", mZygoteUid, mZygoteUid, null, // gids - mZygoteRuntimeFlags, // runtimeFlags + runtimeFlags, "app_zygote", // seInfo abi, // abi abi, // acceptedAbiList diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index 2bfbe4bdfba75..bc7a5fda6f7af 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.Zygote; /** @hide */ public class WebViewZygote { @@ -127,13 +128,15 @@ public class WebViewZygote { try { String abi = sPackage.applicationInfo.primaryCpuAbi; + int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote( + sPackage.applicationInfo, null); sZygote = Process.ZYGOTE_PROCESS.startChildZygote( "com.android.internal.os.WebViewZygoteInit", "webview_zygote", Process.WEBVIEW_ZYGOTE_UID, Process.WEBVIEW_ZYGOTE_UID, null, // gids - 0, // runtimeFlags + runtimeFlags, "webview_zygote", // seInfo abi, // abi TextUtils.join(",", Build.SUPPORTED_ABIS), diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 6d4b8c5ea1adc..b1e7d15cbf4a9 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -20,13 +20,21 @@ import static android.system.OsConstants.O_CLOEXEC; import android.annotation.NonNull; import android.annotation.Nullable; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledAfter; +import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.ProcessInfo; import android.net.Credentials; import android.net.LocalServerSocket; import android.net.LocalSocket; +import android.os.Build; import android.os.FactoryTest; import android.os.IVold; import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemProperties; import android.os.Trace; import android.provider.DeviceConfig; @@ -34,6 +42,7 @@ import android.system.ErrnoException; import android.system.Os; import android.util.Log; +import com.android.internal.compat.IPlatformCompat; import com.android.internal.net.NetworkUtilsInternal; import dalvik.annotation.optimization.CriticalNative; @@ -125,6 +134,7 @@ public final class Zygote { public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20); public static final int MEMORY_TAG_LEVEL_NONE = 0; + /** * Enable pointer tagging in this process. * Tags are checked during memory deallocation, but not on access. @@ -170,10 +180,8 @@ public final class Zygote { */ public static final int GWP_ASAN_LEVEL_ALWAYS = 1 << 22; - /** - * Enable automatic zero-initialization of native heap memory allocations. - */ - public static final int NATIVE_HEAP_ZERO_INIT = 1 << 23; + /** Enable automatic zero-initialization of native heap memory allocations. */ + public static final int NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23; /** * Enable profiling from system services. This loads profiling related plugins in ART. @@ -1170,4 +1178,251 @@ public final class Zygote { * we failed to determine the level. */ public static native int nativeCurrentTaggingLevel(); + + /** + * Native heap allocations will now have a non-zero tag in the most significant byte. + * + * @see Tagged + * Pointers + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. + + /** + * Native heap allocations in AppZygote process and its descendants will now have a non-zero tag + * in the most significant byte. + * + * @see Tagged + * Pointers + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) + private static final long NATIVE_HEAP_POINTER_TAGGING_SECONDARY_ZYGOTE = 207557677; + + /** + * Enable asynchronous (ASYNC) memory tag checking in this process. This flag will only have an + * effect on hardware supporting the ARM Memory Tagging Extension (MTE). + */ + @ChangeId @Disabled + private static final long NATIVE_MEMTAG_ASYNC = 135772972; // This is a bug id. + + /** + * Enable synchronous (SYNC) memory tag checking in this process. This flag will only have an + * effect on hardware supporting the ARM Memory Tagging Extension (MTE). If both + * NATIVE_MEMTAG_ASYNC and this option is selected, this option takes preference and MTE is + * enabled in SYNC mode. + */ + @ChangeId @Disabled + private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id. + + /** Enable automatic zero-initialization of native heap memory allocations. */ + @ChangeId @Disabled + private static final long NATIVE_HEAP_ZERO_INIT = 178038272; // This is a bug id. + + /** + * Enable sampled memory bug detection in the app. + * + * @see GWP-ASan. + */ + @ChangeId @Disabled private static final long GWP_ASAN = 135634846; // This is a bug id. + + private static int memtagModeToZygoteMemtagLevel(int memtagMode) { + switch (memtagMode) { + case ApplicationInfo.MEMTAG_ASYNC: + return MEMORY_TAG_LEVEL_ASYNC; + case ApplicationInfo.MEMTAG_SYNC: + return MEMORY_TAG_LEVEL_SYNC; + default: + return MEMORY_TAG_LEVEL_NONE; + } + } + + private static boolean isCompatChangeEnabled( + long change, + @NonNull ApplicationInfo info, + @Nullable IPlatformCompat platformCompat, + int enabledAfter) { + try { + if (platformCompat != null) return platformCompat.isChangeEnabled(change, info); + } catch (RemoteException ignore) { + } + return enabledAfter > 0 && info.targetSdkVersion > enabledAfter; + } + + // Returns the requested memory tagging level. + private static int getRequestedMemtagLevel( + @NonNull ApplicationInfo info, + @Nullable ProcessInfo processInfo, + @Nullable IPlatformCompat platformCompat) { + // Look at the process attribute first. + if (processInfo != null && processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) { + return memtagModeToZygoteMemtagLevel(processInfo.memtagMode); + } + + // Then at the application attribute. + if (info.getMemtagMode() != ApplicationInfo.MEMTAG_DEFAULT) { + return memtagModeToZygoteMemtagLevel(info.getMemtagMode()); + } + + if (isCompatChangeEnabled(NATIVE_MEMTAG_SYNC, info, platformCompat, 0)) { + return MEMORY_TAG_LEVEL_SYNC; + } + + if (isCompatChangeEnabled(NATIVE_MEMTAG_ASYNC, info, platformCompat, 0)) { + return MEMORY_TAG_LEVEL_ASYNC; + } + + // Check to ensure the app hasn't explicitly opted-out of TBI via. the manifest attribute. + if (!info.allowsNativeHeapPointerTagging()) { + return MEMORY_TAG_LEVEL_NONE; + } + + String defaultLevel = SystemProperties.get("persist.arm64.memtag.app_default"); + if ("sync".equals(defaultLevel)) { + return MEMORY_TAG_LEVEL_SYNC; + } else if ("async".equals(defaultLevel)) { + return MEMORY_TAG_LEVEL_ASYNC; + } + + // Check to see that the compat feature for TBI is enabled. + if (isCompatChangeEnabled( + NATIVE_HEAP_POINTER_TAGGING, info, platformCompat, Build.VERSION_CODES.Q)) { + return MEMORY_TAG_LEVEL_TBI; + } + + return MEMORY_TAG_LEVEL_NONE; + } + + private static int decideTaggingLevel( + @NonNull ApplicationInfo info, + @Nullable ProcessInfo processInfo, + @Nullable IPlatformCompat platformCompat) { + // Get the desired tagging level (app manifest + compat features). + int level = getRequestedMemtagLevel(info, processInfo, platformCompat); + + // Take into account the hardware capabilities. + if (nativeSupportsMemoryTagging()) { + // MTE devices can not do TBI, because the Zygote process already has live MTE + // allocations. Downgrade TBI to NONE. + if (level == MEMORY_TAG_LEVEL_TBI) { + level = MEMORY_TAG_LEVEL_NONE; + } + } else if (nativeSupportsTaggedPointers()) { + // TBI-but-not-MTE devices downgrade MTE modes to TBI. + // The idea is that if an app opts into full hardware tagging (MTE), it must be ok with + // the "fake" pointer tagging (TBI). + if (level == MEMORY_TAG_LEVEL_ASYNC || level == MEMORY_TAG_LEVEL_SYNC) { + level = MEMORY_TAG_LEVEL_TBI; + } + } else { + // Otherwise disable all tagging. + level = MEMORY_TAG_LEVEL_NONE; + } + + return level; + } + + private static int decideGwpAsanLevel( + @NonNull ApplicationInfo info, + @Nullable ProcessInfo processInfo, + @Nullable IPlatformCompat platformCompat) { + // Look at the process attribute first. + if (processInfo != null && processInfo.gwpAsanMode != ApplicationInfo.GWP_ASAN_DEFAULT) { + return processInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_ALWAYS + ? GWP_ASAN_LEVEL_ALWAYS + : GWP_ASAN_LEVEL_NEVER; + } + // Then at the application attribute. + if (info.getGwpAsanMode() != ApplicationInfo.GWP_ASAN_DEFAULT) { + return info.getGwpAsanMode() == ApplicationInfo.GWP_ASAN_ALWAYS + ? GWP_ASAN_LEVEL_ALWAYS + : GWP_ASAN_LEVEL_NEVER; + } + // If the app does not specify gwpAsanMode, the default behavior is lottery among the + // system apps, and disabled for user apps, unless overwritten by the compat feature. + if (isCompatChangeEnabled(GWP_ASAN, info, platformCompat, 0)) { + return GWP_ASAN_LEVEL_ALWAYS; + } + if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + return GWP_ASAN_LEVEL_LOTTERY; + } + return GWP_ASAN_LEVEL_NEVER; + } + + private static boolean enableNativeHeapZeroInit( + @NonNull ApplicationInfo info, + @Nullable ProcessInfo processInfo, + @Nullable IPlatformCompat platformCompat) { + // Look at the process attribute first. + if (processInfo != null + && processInfo.nativeHeapZeroInitialized != ApplicationInfo.ZEROINIT_DEFAULT) { + return processInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_ENABLED; + } + // Then at the application attribute. + if (info.getNativeHeapZeroInitialized() != ApplicationInfo.ZEROINIT_DEFAULT) { + return info.getNativeHeapZeroInitialized() == ApplicationInfo.ZEROINIT_ENABLED; + } + // Compat feature last. + if (isCompatChangeEnabled(NATIVE_HEAP_ZERO_INIT, info, platformCompat, 0)) { + return true; + } + return false; + } + + /** + * Returns Zygote runtimeFlags for memory safety features (MTE, GWP-ASan, nativeHeadZeroInit) + * for a given app. + */ + public static int getMemorySafetyRuntimeFlags( + @NonNull ApplicationInfo info, + @Nullable ProcessInfo processInfo, + @Nullable String instructionSet, + @Nullable IPlatformCompat platformCompat) { + int runtimeFlags = decideGwpAsanLevel(info, processInfo, platformCompat); + // If instructionSet is non-null, this indicates that the system_server is spawning a + // process with an ISA that may be different from its own. System (kernel and hardware) + // compatibility for these features is checked in the decideTaggingLevel in the + // system_server process (not the child process). As both MTE and TBI are only supported + // in aarch64, we can simply ensure that the new process is also aarch64. This prevents + // the mismatch where a 64-bit system server spawns a 32-bit child that thinks it should + // enable some tagging variant. Theoretically, a 32-bit system server could exist that + // spawns 64-bit processes, in which case the new process won't get any tagging. This is + // fine as we haven't seen this configuration in practice, and we can reasonable assume + // that if tagging is desired, the system server will be 64-bit. + if (instructionSet == null || instructionSet.equals("arm64")) { + runtimeFlags |= decideTaggingLevel(info, processInfo, platformCompat); + } + if (enableNativeHeapZeroInit(info, processInfo, platformCompat)) { + runtimeFlags |= NATIVE_HEAP_ZERO_INIT_ENABLED; + } + return runtimeFlags; + } + + /** + * Returns Zygote runtimeFlags for memory safety features (MTE, GWP-ASan, nativeHeadZeroInit) + * for a secondary zygote (AppZygote or WebViewZygote). + */ + public static int getMemorySafetyRuntimeFlagsForSecondaryZygote( + @NonNull ApplicationInfo info, @Nullable ProcessInfo processInfo) { + final IPlatformCompat platformCompat = + IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + int runtimeFlags = + getMemorySafetyRuntimeFlags( + info, processInfo, null /*instructionSet*/, platformCompat); + + // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature. + if ((runtimeFlags & MEMORY_TAG_LEVEL_MASK) == MEMORY_TAG_LEVEL_TBI + && isCompatChangeEnabled( + NATIVE_HEAP_POINTER_TAGGING_SECONDARY_ZYGOTE, + info, + platformCompat, + Build.VERSION_CODES.S)) { + // Reset memory tag level to NONE. + runtimeFlags &= ~MEMORY_TAG_LEVEL_MASK; + runtimeFlags |= MEMORY_TAG_LEVEL_NONE; + } + return runtimeFlags; + } } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 597167026d19f..5b7092cabfcbb 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -346,7 +346,7 @@ enum RuntimeFlags : uint32_t { GWP_ASAN_LEVEL_NEVER = 0 << 21, GWP_ASAN_LEVEL_LOTTERY = 1 << 21, GWP_ASAN_LEVEL_ALWAYS = 2 << 21, - NATIVE_HEAP_ZERO_INIT = 1 << 23, + NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23, PROFILEABLE = 1 << 24, }; @@ -1709,13 +1709,13 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // would be nice to have them for apps, we will have to wait until they are // proven out, have more efficient hardware, and/or apply them only to new // applications. - if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT)) { + if (!(runtime_flags & RuntimeFlags::NATIVE_HEAP_ZERO_INIT_ENABLED)) { mallopt(M_BIONIC_ZERO_INIT, 0); } // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART // runtime. - runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT; + runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT_ENABLED; bool forceEnableGwpAsan = false; switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 38f6e6d9f165b..d4ad718fbe730 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -4139,7 +4139,8 @@ public final class ActiveServices { final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; final String procName = r.processName; - HostingRecord hostingRecord = new HostingRecord("service", r.instanceName); + HostingRecord hostingRecord = new HostingRecord("service", r.instanceName, + r.definingPackageName, r.definingUid, r.serviceInfo.processName); ProcessRecord app; if (!isolated) { @@ -4177,11 +4178,12 @@ public final class ActiveServices { app = r.isolationHostProc; if (WebViewZygote.isMultiprocessEnabled() && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) { - hostingRecord = HostingRecord.byWebviewZygote(r.instanceName); + hostingRecord = HostingRecord.byWebviewZygote(r.instanceName, r.definingPackageName, + r.definingUid, r.serviceInfo.processName); } if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) { hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName, - r.definingUid); + r.definingUid, r.serviceInfo.processName); } } diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java index 6bb5def26b9d0..bbf586123e1c3 100644 --- a/services/core/java/com/android/server/am/HostingRecord.java +++ b/services/core/java/com/android/server/am/HostingRecord.java @@ -56,19 +56,27 @@ public final class HostingRecord { private final String mDefiningPackageName; private final int mDefiningUid; private final boolean mIsTopApp; + private final String mDefiningProcessName; public HostingRecord(String hostingType) { this(hostingType, null /* hostingName */, REGULAR_ZYGOTE, null /* definingPackageName */, - -1 /* mDefiningUid */, false /* isTopApp */); + -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */); } public HostingRecord(String hostingType, ComponentName hostingName) { this(hostingType, hostingName, REGULAR_ZYGOTE); } + public HostingRecord(String hostingType, ComponentName hostingName, String definingPackageName, + int definingUid, String definingProcessName) { + this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, definingPackageName, + definingUid, false /* isTopApp */, definingProcessName); + } + public HostingRecord(String hostingType, ComponentName hostingName, boolean isTopApp) { this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, - null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */); + null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */, + null /* definingProcessName */); } public HostingRecord(String hostingType, String hostingName) { @@ -81,17 +89,19 @@ public final class HostingRecord { private HostingRecord(String hostingType, String hostingName, int hostingZygote) { this(hostingType, hostingName, hostingZygote, null /* definingPackageName */, - -1 /* mDefiningUid */, false /* isTopApp */); + -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */); } private HostingRecord(String hostingType, String hostingName, int hostingZygote, - String definingPackageName, int definingUid, boolean isTopApp) { + String definingPackageName, int definingUid, boolean isTopApp, + String definingProcessName) { mHostingType = hostingType; mHostingName = hostingName; mHostingZygote = hostingZygote; mDefiningPackageName = definingPackageName; mDefiningUid = definingUid; mIsTopApp = isTopApp; + mDefiningProcessName = definingProcessName; } public String getType() { @@ -126,13 +136,25 @@ public final class HostingRecord { return mDefiningPackageName; } + /** + * Returns the processName of the component we want to start as specified in the defining app's + * manifest. + * + * @return the processName of the process in the hosting application + */ + public String getDefiningProcessName() { + return mDefiningProcessName; + } + /** * Creates a HostingRecord for a process that must spawn from the webview zygote * @param hostingName name of the component to be hosted in this process * @return The constructed HostingRecord */ - public static HostingRecord byWebviewZygote(ComponentName hostingName) { - return new HostingRecord("", hostingName.toShortString(), WEBVIEW_ZYGOTE); + public static HostingRecord byWebviewZygote(ComponentName hostingName, + String definingPackageName, int definingUid, String definingProcessName) { + return new HostingRecord("", hostingName.toShortString(), WEBVIEW_ZYGOTE, + definingPackageName, definingUid, false /* isTopApp */, definingProcessName); } /** @@ -143,9 +165,9 @@ public final class HostingRecord { * @return The constructed HostingRecord */ public static HostingRecord byAppZygote(ComponentName hostingName, String definingPackageName, - int definingUid) { + int definingUid, String definingProcessName) { return new HostingRecord("", hostingName.toShortString(), APP_ZYGOTE, - definingPackageName, definingUid, false /* isTopApp */); + definingPackageName, definingUid, false /* isTopApp */, definingProcessName); } /** diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 41cd61da022a0..2c2e7c40c9c35 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -70,7 +70,6 @@ import android.app.IApplicationThread; import android.app.IProcessObserver; import android.app.IUidObserver; import android.compat.annotation.ChangeId; -import android.compat.annotation.Disabled; import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -362,59 +361,6 @@ public final class ProcessList { // lmkd reconnect delay in msecs private static final long LMKD_RECONNECT_DELAY_MS = 1000; - /** - * Native heap allocations will now have a non-zero tag in the most significant byte. - * @see Tagged - * Pointers - */ - @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) - private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. - - /** - * Native heap allocations in AppZygote process and its descendants will now have a - * non-zero tag in the most significant byte. - * @see Tagged - * Pointers - */ - @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) - private static final long NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE = 207557677; - - /** - * Enable asynchronous (ASYNC) memory tag checking in this process. This - * flag will only have an effect on hardware supporting the ARM Memory - * Tagging Extension (MTE). - */ - @ChangeId - @Disabled - private static final long NATIVE_MEMTAG_ASYNC = 135772972; // This is a bug id. - - /** - * Enable synchronous (SYNC) memory tag checking in this process. This flag - * will only have an effect on hardware supporting the ARM Memory Tagging - * Extension (MTE). If both NATIVE_MEMTAG_ASYNC and this option is selected, - * this option takes preference and MTE is enabled in SYNC mode. - */ - @ChangeId - @Disabled - private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id. - - /** - * Enable automatic zero-initialization of native heap memory allocations. - */ - @ChangeId - @Disabled - private static final long NATIVE_HEAP_ZERO_INIT = 178038272; // This is a bug id. - - /** - * Enable sampled memory bug detection in the app. - * @see GWP-ASan. - */ - @ChangeId - @Disabled - private static final long GWP_ASAN = 135634846; // This is a bug id. - /** * Apps have no access to the private data directories of any other app, even if the other * app has made them world-readable. @@ -1681,136 +1627,6 @@ public final class ProcessList { return gidArray; } - private int memtagModeToZygoteMemtagLevel(int memtagMode) { - switch (memtagMode) { - case ApplicationInfo.MEMTAG_ASYNC: - return Zygote.MEMORY_TAG_LEVEL_ASYNC; - case ApplicationInfo.MEMTAG_SYNC: - return Zygote.MEMORY_TAG_LEVEL_SYNC; - default: - return Zygote.MEMORY_TAG_LEVEL_NONE; - } - } - - // Returns the requested memory tagging level. - private int getRequestedMemtagLevel(ProcessRecord app) { - // Look at the process attribute first. - if (app.processInfo != null - && app.processInfo.memtagMode != ApplicationInfo.MEMTAG_DEFAULT) { - return memtagModeToZygoteMemtagLevel(app.processInfo.memtagMode); - } - - // Then at the application attribute. - if (app.info.getMemtagMode() != ApplicationInfo.MEMTAG_DEFAULT) { - return memtagModeToZygoteMemtagLevel(app.info.getMemtagMode()); - } - - if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_SYNC, app.info)) { - return Zygote.MEMORY_TAG_LEVEL_SYNC; - } - - if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_ASYNC, app.info)) { - return Zygote.MEMORY_TAG_LEVEL_ASYNC; - } - - // Check to ensure the app hasn't explicitly opted-out of TBI via. the manifest attribute. - if (!app.info.allowsNativeHeapPointerTagging()) { - return Zygote.MEMORY_TAG_LEVEL_NONE; - } - - String defaultLevel = SystemProperties.get("persist.arm64.memtag.app_default"); - if ("sync".equals(defaultLevel)) { - return Zygote.MEMORY_TAG_LEVEL_SYNC; - } else if ("async".equals(defaultLevel)) { - return Zygote.MEMORY_TAG_LEVEL_ASYNC; - } - - // Check to see that the compat feature for TBI is enabled. - if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) { - return Zygote.MEMORY_TAG_LEVEL_TBI; - } - - return Zygote.MEMORY_TAG_LEVEL_NONE; - } - - private int decideTaggingLevel(ProcessRecord app) { - // Get the desired tagging level (app manifest + compat features). - int level = getRequestedMemtagLevel(app); - - // Take into account the hardware capabilities. - if (Zygote.nativeSupportsMemoryTagging()) { - // MTE devices can not do TBI, because the Zygote process already has live MTE - // allocations. Downgrade TBI to NONE. - if (level == Zygote.MEMORY_TAG_LEVEL_TBI) { - level = Zygote.MEMORY_TAG_LEVEL_NONE; - } - } else if (Zygote.nativeSupportsTaggedPointers()) { - // TBI-but-not-MTE devices downgrade MTE modes to TBI. - // The idea is that if an app opts into full hardware tagging (MTE), it must be ok with - // the "fake" pointer tagging (TBI). - if (level == Zygote.MEMORY_TAG_LEVEL_ASYNC || level == Zygote.MEMORY_TAG_LEVEL_SYNC) { - level = Zygote.MEMORY_TAG_LEVEL_TBI; - } - } else { - // Otherwise disable all tagging. - level = Zygote.MEMORY_TAG_LEVEL_NONE; - } - - return level; - } - - private int decideTaggingLevelForAppZygote(ProcessRecord app) { - int level = decideTaggingLevel(app); - // TBI ("fake" pointer tagging) in AppZygote is controlled by a separate compat feature. - if (!mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING_APP_ZYGOTE, app.info) - && level == Zygote.MEMORY_TAG_LEVEL_TBI) { - level = Zygote.MEMORY_TAG_LEVEL_NONE; - } - return level; - } - - private int decideGwpAsanLevel(ProcessRecord app) { - // Look at the process attribute first. - if (app.processInfo != null - && app.processInfo.gwpAsanMode != ApplicationInfo.GWP_ASAN_DEFAULT) { - return app.processInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_ALWAYS - ? Zygote.GWP_ASAN_LEVEL_ALWAYS - : Zygote.GWP_ASAN_LEVEL_NEVER; - } - // Then at the application attribute. - if (app.info.getGwpAsanMode() != ApplicationInfo.GWP_ASAN_DEFAULT) { - return app.info.getGwpAsanMode() == ApplicationInfo.GWP_ASAN_ALWAYS - ? Zygote.GWP_ASAN_LEVEL_ALWAYS - : Zygote.GWP_ASAN_LEVEL_NEVER; - } - // If the app does not specify gwpAsanMode, the default behavior is lottery among the - // system apps, and disabled for user apps, unless overwritten by the compat feature. - if (mPlatformCompat.isChangeEnabled(GWP_ASAN, app.info)) { - return Zygote.GWP_ASAN_LEVEL_ALWAYS; - } - if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - return Zygote.GWP_ASAN_LEVEL_LOTTERY; - } - return Zygote.GWP_ASAN_LEVEL_NEVER; - } - - private boolean enableNativeHeapZeroInit(ProcessRecord app) { - // Look at the process attribute first. - if (app.processInfo != null - && app.processInfo.nativeHeapZeroInitialized != ApplicationInfo.ZEROINIT_DEFAULT) { - return app.processInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_ENABLED; - } - // Then at the application attribute. - if (app.info.getNativeHeapZeroInitialized() != ApplicationInfo.ZEROINIT_DEFAULT) { - return app.info.getNativeHeapZeroInitialized() == ApplicationInfo.ZEROINIT_ENABLED; - } - // Compat feature last. - if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_ZERO_INIT, app.info)) { - return true; - } - return false; - } - /** * @return {@code true} if process start is successful, false otherwise. */ @@ -1992,8 +1808,6 @@ public final class ProcessList { runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE; } - runtimeFlags |= decideGwpAsanLevel(app); - String invokeWith = null; if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { // Debuggable apps may include a wrapper script with their library directory. @@ -2024,23 +1838,21 @@ public final class ProcessList { app.setRequiredAbi(requiredAbi); app.setInstructionSet(instructionSet); - // If instructionSet is non-null, this indicates that the system_server is spawning a - // process with an ISA that may be different from its own. System (kernel and hardware) - // compatibility for these features is checked in the decideTaggingLevel in the - // system_server process (not the child process). As both MTE and TBI are only supported - // in aarch64, we can simply ensure that the new process is also aarch64. This prevents - // the mismatch where a 64-bit system server spawns a 32-bit child that thinks it should - // enable some tagging variant. Theoretically, a 32-bit system server could exist that - // spawns 64-bit processes, in which case the new process won't get any tagging. This is - // fine as we haven't seen this configuration in practice, and we can reasonable assume - // that if tagging is desired, the system server will be 64-bit. - if (instructionSet == null || instructionSet.equals("arm64")) { - runtimeFlags |= decideTaggingLevel(app); + // If this was an external service, the package name and uid in the passed in + // ApplicationInfo have been changed to match those of the calling package; + // that will incorrectly apply compat feature overrides for the calling package instead + // of the defining one. + ApplicationInfo definingAppInfo; + if (hostingRecord.getDefiningPackageName() != null) { + definingAppInfo = new ApplicationInfo(app.info); + definingAppInfo.packageName = hostingRecord.getDefiningPackageName(); + definingAppInfo.uid = uid; + } else { + definingAppInfo = app.info; } - if (enableNativeHeapZeroInit(app)) { - runtimeFlags |= Zygote.NATIVE_HEAP_ZERO_INIT; - } + runtimeFlags |= Zygote.getMemorySafetyRuntimeFlags( + definingAppInfo, app.processInfo, instructionSet, mPlatformCompat); // the per-user SELinux context must be set if (TextUtils.isEmpty(app.info.seInfoUser)) { @@ -2299,8 +2111,7 @@ public final class ProcessList { // not the calling one. appInfo.packageName = app.getHostingRecord().getDefiningPackageName(); appInfo.uid = uid; - int runtimeFlags = decideTaggingLevelForAppZygote(app); - appZygote = new AppZygote(appInfo, uid, firstUid, lastUid, runtimeFlags); + appZygote = new AppZygote(appInfo, app.processInfo, uid, firstUid, lastUid); mAppZygotes.put(app.info.processName, uid, appZygote); zygoteProcessList = new ArrayList(); mAppZygoteProcesses.put(appZygote, zygoteProcessList); @@ -3158,7 +2969,8 @@ public final class ProcessList { FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, info.uid, uid, FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED); } - final ProcessRecord r = new ProcessRecord(mService, info, proc, uid); + final ProcessRecord r = new ProcessRecord(mService, info, proc, uid, + hostingRecord.getDefiningUid(), hostingRecord.getDefiningProcessName()); final ProcessStateRecord state = r.mState; if (!mService.mBooted && !mService.mBooting diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index be187e21db47d..7672d10e27bd0 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -492,24 +492,33 @@ class ProcessRecord implements WindowProcessListener { ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName, int _uid) { + this(_service, _info, _processName, _uid, -1, null); + } + + ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName, + int _uid, int _definingUid, String _definingProcessName) { mService = _service; mProcLock = _service.mProcLock; info = _info; ProcessInfo procInfo = null; if (_service.mPackageManagerInt != null) { - ArrayMap processes = - _service.mPackageManagerInt.getProcessesForUid(_uid); - if (processes != null) { - procInfo = processes.get(_processName); - if (procInfo != null && procInfo.deniedPermissions == null - && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT - && procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT - && procInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_DEFAULT) { - // If this process hasn't asked for permissions to be denied, or for a - // non-default GwpAsan mode, or any other non-default setting, then we don't - // care about it. - procInfo = null; - } + if (_definingUid > 0) { + ArrayMap processes = + _service.mPackageManagerInt.getProcessesForUid(_definingUid); + if (processes != null) procInfo = processes.get(_definingProcessName); + } else { + ArrayMap processes = + _service.mPackageManagerInt.getProcessesForUid(_uid); + if (processes != null) procInfo = processes.get(_processName); + } + if (procInfo != null && procInfo.deniedPermissions == null + && procInfo.gwpAsanMode == ApplicationInfo.GWP_ASAN_DEFAULT + && procInfo.memtagMode == ApplicationInfo.MEMTAG_DEFAULT + && procInfo.nativeHeapZeroInitialized == ApplicationInfo.ZEROINIT_DEFAULT) { + // If this process hasn't asked for permissions to be denied, or for a + // non-default GwpAsan mode, or any other non-default setting, then we don't + // care about it. + procInfo = null; } } processInfo = procInfo; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index 26b5218d2ab4f..4a40b5f2de7b2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -1000,7 +1000,7 @@ public class ApplicationExitInfoTest { final String dummyPackageName = "com.android.test"; final String dummyClassName = ".Foo"; app.setHostingRecord(HostingRecord.byAppZygote(new ComponentName( - dummyPackageName, dummyClassName), "", definingUid)); + dummyPackageName, dummyClassName), "", definingUid, "")); } app.mServices.setConnectionGroup(connectionGroup); app.mState.setReportedProcState(procState);