1501 lines
64 KiB
Java
1501 lines
64 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2014 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package com.android.internal.os;
|
||
|
|
||
|
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;
|
||
|
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;
|
||
|
import dalvik.annotation.optimization.FastNative;
|
||
|
import dalvik.system.ZygoteHooks;
|
||
|
|
||
|
import libcore.io.IoUtils;
|
||
|
|
||
|
import java.io.ByteArrayOutputStream;
|
||
|
import java.io.DataOutputStream;
|
||
|
import java.io.FileDescriptor;
|
||
|
import java.io.IOException;
|
||
|
|
||
|
/** @hide */
|
||
|
public final class Zygote {
|
||
|
/*
|
||
|
* Bit values for "runtimeFlags" argument. The definitions are duplicated
|
||
|
* in the native code.
|
||
|
*/
|
||
|
|
||
|
/** enable debugging over JDWP */
|
||
|
public static final int DEBUG_ENABLE_JDWP = 1;
|
||
|
/** enable JNI checks */
|
||
|
public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1;
|
||
|
/** enable Java programming language "assert" statements */
|
||
|
public static final int DEBUG_ENABLE_ASSERT = 1 << 2;
|
||
|
/** disable the AOT compiler and JIT */
|
||
|
public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3;
|
||
|
/** Enable logging of third-party JNI activity. */
|
||
|
public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
|
||
|
/** Force generation of native debugging information. */
|
||
|
public static final int DEBUG_GENERATE_DEBUG_INFO = 1 << 5;
|
||
|
/** Always use JIT-ed code. */
|
||
|
public static final int DEBUG_ALWAYS_JIT = 1 << 6;
|
||
|
/** Make the code native debuggable by turning off some optimizations. */
|
||
|
public static final int DEBUG_NATIVE_DEBUGGABLE = 1 << 7;
|
||
|
/** Make the code Java debuggable by turning off some optimizations. */
|
||
|
public static final int DEBUG_JAVA_DEBUGGABLE = 1 << 8;
|
||
|
|
||
|
/** Turn off the verifier. */
|
||
|
public static final int DISABLE_VERIFIER = 1 << 9;
|
||
|
/** Only use oat files located in /system. Otherwise use dex/jar/apk . */
|
||
|
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
|
||
|
/** Force generation of native debugging information for backtraces. */
|
||
|
public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11;
|
||
|
/**
|
||
|
* Hidden API access restrictions. This is a mask for bits representing the API enforcement
|
||
|
* policy, defined by {@code @ApplicationInfo.HiddenApiEnforcementPolicy}.
|
||
|
*/
|
||
|
public static final int API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13);
|
||
|
/**
|
||
|
* Bit shift for use with {@link #API_ENFORCEMENT_POLICY_MASK}.
|
||
|
*
|
||
|
* (flags & API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT gives
|
||
|
* {@link ApplicationInfo.HiddenApiEnforcementPolicy} values.
|
||
|
*/
|
||
|
public static final int API_ENFORCEMENT_POLICY_SHIFT =
|
||
|
Integer.numberOfTrailingZeros(API_ENFORCEMENT_POLICY_MASK);
|
||
|
/**
|
||
|
* Enable system server ART profiling.
|
||
|
*/
|
||
|
public static final int PROFILE_SYSTEM_SERVER = 1 << 14;
|
||
|
|
||
|
/**
|
||
|
* Enable profiling from shell.
|
||
|
*/
|
||
|
public static final int PROFILE_FROM_SHELL = 1 << 15;
|
||
|
|
||
|
/*
|
||
|
* Enable using the ART app image startup cache
|
||
|
*/
|
||
|
public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
|
||
|
|
||
|
/**
|
||
|
* When set, application specified signal handlers are not chained (i.e, ignored)
|
||
|
* by the runtime.
|
||
|
*
|
||
|
* Used for debugging only. Usage: set debug.ignoreappsignalhandler to 1.
|
||
|
*/
|
||
|
public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
|
||
|
|
||
|
/**
|
||
|
* Disable runtime access to {@link android.annotation.TestApi} annotated members.
|
||
|
*
|
||
|
* <p>This only takes effect if Hidden API access restrictions are enabled as well.
|
||
|
*/
|
||
|
public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
|
||
|
|
||
|
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.
|
||
|
* TBI stands for Top-Byte-Ignore, an ARM CPU feature.
|
||
|
* {@link https://developer.arm.com/docs/den0024/latest/the-memory-management-unit/translation-table-configuration/virtual-address-tagging}
|
||
|
*/
|
||
|
public static final int MEMORY_TAG_LEVEL_TBI = 1 << 19;
|
||
|
|
||
|
/**
|
||
|
* Enable asynchronous memory tag checks in this process.
|
||
|
*/
|
||
|
public static final int MEMORY_TAG_LEVEL_ASYNC = 2 << 19;
|
||
|
|
||
|
/**
|
||
|
* Enable synchronous memory tag checks in this process.
|
||
|
*/
|
||
|
public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19;
|
||
|
|
||
|
/**
|
||
|
* A two-bit field for GWP-ASan level of this process. See the possible values below.
|
||
|
*/
|
||
|
public static final int GWP_ASAN_LEVEL_MASK = (1 << 21) | (1 << 22);
|
||
|
|
||
|
/**
|
||
|
* Disable GWP-ASan in this process.
|
||
|
* GWP-ASan is a low-overhead memory bug detector using guard pages on a small
|
||
|
* subset of heap allocations.
|
||
|
*/
|
||
|
public static final int GWP_ASAN_LEVEL_NEVER = 0 << 21;
|
||
|
|
||
|
/**
|
||
|
* Enable GWP-ASan in this process with a small sampling rate.
|
||
|
* With approx. 1% chance GWP-ASan will be activated and apply its protection
|
||
|
* to a small subset of heap allocations.
|
||
|
* Otherwise (~99% chance) this process is unaffected.
|
||
|
*/
|
||
|
public static final int GWP_ASAN_LEVEL_LOTTERY = 1 << 21;
|
||
|
|
||
|
/**
|
||
|
* Always enable GWP-ASan in this process.
|
||
|
* GWP-ASan is activated unconditionally (but still, only a small subset of
|
||
|
* allocations is protected).
|
||
|
*/
|
||
|
public static final int GWP_ASAN_LEVEL_ALWAYS = 2 << 21;
|
||
|
|
||
|
/**
|
||
|
* GWP-ASan's `gwpAsanMode` manifest flag was unspecified. Currently, this
|
||
|
* means GWP_ASAN_LEVEL_LOTTERY for system apps, and GWP_ASAN_LEVEL_NONE for
|
||
|
* non-system apps.
|
||
|
*/
|
||
|
public static final int GWP_ASAN_LEVEL_DEFAULT = 3 << 21;
|
||
|
|
||
|
/** 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.
|
||
|
*/
|
||
|
public static final int PROFILEABLE = 1 << 24;
|
||
|
|
||
|
/**
|
||
|
* Enable ptrace. This is enabled on eng, if the app is debuggable, or if
|
||
|
* the persist.debug.ptrace.enabled property is set.
|
||
|
*/
|
||
|
public static final int DEBUG_ENABLE_PTRACE = 1 << 25;
|
||
|
|
||
|
/** No external storage should be mounted. */
|
||
|
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
|
||
|
/** Default external storage should be mounted. */
|
||
|
public static final int MOUNT_EXTERNAL_DEFAULT = IVold.REMOUNT_MODE_DEFAULT;
|
||
|
/**
|
||
|
* Mount mode for package installers which should give them access to
|
||
|
* all obb dirs in addition to their package sandboxes
|
||
|
*/
|
||
|
public static final int MOUNT_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
|
||
|
/** The lower file system should be bind mounted directly on external storage */
|
||
|
public static final int MOUNT_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH;
|
||
|
|
||
|
/** Use the regular scoped storage filesystem, but Android/ should be writable.
|
||
|
* Used to support the applications hosting DownloadManager and the MTP server.
|
||
|
*/
|
||
|
public static final int MOUNT_EXTERNAL_ANDROID_WRITABLE = IVold.REMOUNT_MODE_ANDROID_WRITABLE;
|
||
|
|
||
|
/** Number of bytes sent to the Zygote over USAP pipes or the pool event FD */
|
||
|
static final int USAP_MANAGEMENT_MESSAGE_BYTES = 8;
|
||
|
|
||
|
/** Make the new process have top application priority. */
|
||
|
public static final String START_AS_TOP_APP_ARG = "--is-top-app";
|
||
|
|
||
|
/** List of packages with the same uid, and its app data info: volume uuid and inode. */
|
||
|
public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
|
||
|
|
||
|
/** List of allowlisted packages and its app data info: volume uuid and inode. */
|
||
|
public static final String ALLOWLISTED_DATA_INFO_MAP = "--allowlisted-data-info-map";
|
||
|
|
||
|
/** Bind mount app storage dirs to lower fs not via fuse */
|
||
|
public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
|
||
|
|
||
|
/** Bind mount app storage dirs to lower fs not via fuse */
|
||
|
public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs";
|
||
|
|
||
|
/** Bind the system properties to an alternate set, for appcompat reasons */
|
||
|
public static final String BIND_MOUNT_SYSPROP_OVERRIDES = "--bind-mount-sysprop-overrides";
|
||
|
|
||
|
/**
|
||
|
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
|
||
|
* in the abstract socket namespace. This socket name is what the new child zygote
|
||
|
* should listen for connections on.
|
||
|
*/
|
||
|
public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
|
||
|
|
||
|
/**
|
||
|
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
|
||
|
* requested ABI for the child Zygote.
|
||
|
*/
|
||
|
public static final String CHILD_ZYGOTE_ABI_LIST_ARG = "--abi-list=";
|
||
|
|
||
|
/**
|
||
|
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
|
||
|
* start of the UID range the children of the Zygote may setuid()/setgid() to. This
|
||
|
* will be enforced with a seccomp filter.
|
||
|
*/
|
||
|
public static final String CHILD_ZYGOTE_UID_RANGE_START = "--uid-range-start=";
|
||
|
|
||
|
/**
|
||
|
* An extraArg passed when a zygote process is forking a child-zygote, specifying the
|
||
|
* end of the UID range the children of the Zygote may setuid()/setgid() to. This
|
||
|
* will be enforced with a seccomp filter.
|
||
|
*/
|
||
|
public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
|
||
|
|
||
|
private static final String TAG = "Zygote";
|
||
|
|
||
|
/** Prefix prepended to socket names created by init */
|
||
|
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
|
||
|
|
||
|
/**
|
||
|
* The duration to wait before re-checking Zygote related system properties.
|
||
|
*
|
||
|
* One minute in milliseconds.
|
||
|
*/
|
||
|
public static final long PROPERTY_CHECK_INTERVAL = 60000;
|
||
|
|
||
|
/**
|
||
|
* @hide for internal use only
|
||
|
*/
|
||
|
public static final int SOCKET_BUFFER_SIZE = 256;
|
||
|
|
||
|
/**
|
||
|
* @hide for internal use only
|
||
|
*/
|
||
|
private static final int PRIORITY_MAX = -20;
|
||
|
|
||
|
/** a prototype instance for a future List.toArray() */
|
||
|
static final int[][] INT_ARRAY_2D = new int[0][0];
|
||
|
|
||
|
/**
|
||
|
* @hide for internal use only.
|
||
|
*/
|
||
|
public static final String PRIMARY_SOCKET_NAME = "zygote";
|
||
|
|
||
|
/**
|
||
|
* @hide for internal use only.
|
||
|
*/
|
||
|
public static final String SECONDARY_SOCKET_NAME = "zygote_secondary";
|
||
|
|
||
|
/**
|
||
|
* @hide for internal use only
|
||
|
*/
|
||
|
public static final String USAP_POOL_PRIMARY_SOCKET_NAME = "usap_pool_primary";
|
||
|
|
||
|
/**
|
||
|
* @hide for internal use only
|
||
|
*/
|
||
|
public static final String USAP_POOL_SECONDARY_SOCKET_NAME = "usap_pool_secondary";
|
||
|
|
||
|
private Zygote() {}
|
||
|
|
||
|
private static boolean containsInetGid(int[] gids) {
|
||
|
for (int i = 0; i < gids.length; i++) {
|
||
|
if (gids[i] == android.os.Process.INET_GID) return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Forks a new VM instance. The current VM must have been started
|
||
|
* with the -Xzygote flag. <b>NOTE: new instance keeps all
|
||
|
* root capabilities. The new process is expected to call capset()</b>.
|
||
|
*
|
||
|
* @param uid the UNIX uid that the new process should setuid() to after
|
||
|
* fork()ing and and before spawning any threads.
|
||
|
* @param gid the UNIX gid that the new process should setgid() to after
|
||
|
* fork()ing and and before spawning any threads.
|
||
|
* @param gids null-ok; a list of UNIX gids that the new process should
|
||
|
* setgroups() to after fork and before spawning any threads.
|
||
|
* @param runtimeFlags bit flags that enable ART features.
|
||
|
* @param rlimits null-ok an array of rlimit tuples, with the second
|
||
|
* dimension having a length of 3 and representing
|
||
|
* (resource, rlim_cur, rlim_max). These are set via the posix
|
||
|
* setrlimit(2) call.
|
||
|
* @param seInfo null-ok a string specifying SELinux information for
|
||
|
* the new process.
|
||
|
* @param niceName null-ok a string specifying the process name.
|
||
|
* @param fdsToClose an array of ints, holding one or more POSIX
|
||
|
* file descriptor numbers that are to be closed by the child
|
||
|
* (and replaced by /dev/null) after forking. An integer value
|
||
|
* of -1 in any entry in the array means "ignore this one".
|
||
|
* @param fdsToIgnore null-ok an array of ints, either null or holding
|
||
|
* one or more POSIX file descriptor numbers that are to be ignored
|
||
|
* in the file descriptor table check.
|
||
|
* @param startChildZygote if true, the new child process will itself be a
|
||
|
* new zygote process.
|
||
|
* @param instructionSet null-ok the instruction set to use.
|
||
|
* @param appDataDir null-ok the data directory of the app.
|
||
|
* @param isTopApp true if the process is for top (high priority) application.
|
||
|
* @param pkgDataInfoList A list that stores related packages and its app data
|
||
|
* info: volume uuid and inode.
|
||
|
* @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
|
||
|
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
|
||
|
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
|
||
|
* @param bindMountSyspropOverrides True if the zygote needs to mount the override system
|
||
|
* properties
|
||
|
*
|
||
|
* @return 0 if this is the child, pid of the child
|
||
|
* if this is the parent, or -1 on error.
|
||
|
*/
|
||
|
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
|
||
|
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
|
||
|
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
|
||
|
boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList,
|
||
|
boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs,
|
||
|
boolean bindMountSyspropOverrides) {
|
||
|
ZygoteHooks.preFork();
|
||
|
|
||
|
int pid = nativeForkAndSpecialize(
|
||
|
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
|
||
|
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
|
||
|
pkgDataInfoList, allowlistedDataInfoList, bindMountAppDataDirs,
|
||
|
bindMountAppStorageDirs, bindMountSyspropOverrides);
|
||
|
if (pid == 0) {
|
||
|
// Note that this event ends at the end of handleChildProc,
|
||
|
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
|
||
|
|
||
|
// If no GIDs were specified, don't make any permissions changes based on groups.
|
||
|
if (gids != null && gids.length > 0) {
|
||
|
NetworkUtilsInternal.setAllowNetworkingForProcess(containsInetGid(gids));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set the Java Language thread priority to the default value for new apps.
|
||
|
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
|
||
|
|
||
|
ZygoteHooks.postForkCommon();
|
||
|
return pid;
|
||
|
}
|
||
|
|
||
|
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
|
||
|
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
|
||
|
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
|
||
|
String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
|
||
|
String[] allowlistedDataInfoList, boolean bindMountAppDataDirs,
|
||
|
boolean bindMountAppStorageDirs, boolean bindMountSyspropOverrides);
|
||
|
|
||
|
/**
|
||
|
* Specialize an unspecialized app process. The current VM must have been started
|
||
|
* with the -Xzygote flag.
|
||
|
*
|
||
|
* @param uid The UNIX uid that the new process should setuid() to before spawning any threads
|
||
|
* @param gid The UNIX gid that the new process should setgid() to before spawning any threads
|
||
|
* @param gids null-ok; A list of UNIX gids that the new process should
|
||
|
* setgroups() to before spawning any threads
|
||
|
* @param runtimeFlags Bit flags that enable ART features
|
||
|
* @param rlimits null-ok An array of rlimit tuples, with the second
|
||
|
* dimension having a length of 3 and representing
|
||
|
* (resource, rlim_cur, rlim_max). These are set via the posix
|
||
|
* setrlimit(2) call.
|
||
|
* @param seInfo null-ok A string specifying SELinux information for
|
||
|
* the new process.
|
||
|
* @param niceName null-ok A string specifying the process name.
|
||
|
* @param startChildZygote If true, the new child process will itself be a
|
||
|
* new zygote process.
|
||
|
* @param instructionSet null-ok The instruction set to use.
|
||
|
* @param appDataDir null-ok The data directory of the app.
|
||
|
* @param isTopApp True if the process is for top (high priority) application.
|
||
|
* @param pkgDataInfoList A list that stores related packages and its app data
|
||
|
* volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
|
||
|
* app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
|
||
|
* app_b_ce_inode, ...];
|
||
|
* @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps.
|
||
|
* @param bindMountAppDataDirs True if the zygote needs to mount data dirs.
|
||
|
* @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
|
||
|
* @param bindMountSyspropOverrides True if the zygote needs to mount the override system
|
||
|
* properties
|
||
|
*/
|
||
|
private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
|
||
|
int[][] rlimits, int mountExternal, String seInfo, String niceName,
|
||
|
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
|
||
|
String[] pkgDataInfoList, String[] allowlistedDataInfoList,
|
||
|
boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs,
|
||
|
boolean bindMountSyspropOverrides) {
|
||
|
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
|
||
|
niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
|
||
|
pkgDataInfoList, allowlistedDataInfoList,
|
||
|
bindMountAppDataDirs, bindMountAppStorageDirs, bindMountSyspropOverrides);
|
||
|
|
||
|
// Note that this event ends at the end of handleChildProc.
|
||
|
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
|
||
|
|
||
|
if (gids != null && gids.length > 0) {
|
||
|
NetworkUtilsInternal.setAllowNetworkingForProcess(containsInetGid(gids));
|
||
|
}
|
||
|
|
||
|
// Set the Java Language thread priority to the default value for new apps.
|
||
|
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
|
||
|
|
||
|
/*
|
||
|
* This is called here (instead of after the fork but before the specialize) to maintain
|
||
|
* consistancy with the code paths for forkAndSpecialize.
|
||
|
*
|
||
|
* TODO (chriswailes): Look into moving this to immediately after the fork.
|
||
|
*/
|
||
|
ZygoteHooks.postForkCommon();
|
||
|
}
|
||
|
|
||
|
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
|
||
|
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
|
||
|
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
|
||
|
String[] pkgDataInfoList, String[] allowlistedDataInfoList,
|
||
|
boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs,
|
||
|
boolean bindMountSyspropOverrides);
|
||
|
|
||
|
/**
|
||
|
* Called to do any initialization before starting an application.
|
||
|
*/
|
||
|
static native void nativePreApplicationInit();
|
||
|
|
||
|
/**
|
||
|
* Special method to start the system server process. In addition to the
|
||
|
* common actions performed in forkAndSpecialize, the pid of the child
|
||
|
* process is recorded such that the death of the child process will cause
|
||
|
* zygote to exit.
|
||
|
*
|
||
|
* @param uid the UNIX uid that the new process should setuid() to after
|
||
|
* fork()ing and and before spawning any threads.
|
||
|
* @param gid the UNIX gid that the new process should setgid() to after
|
||
|
* fork()ing and and before spawning any threads.
|
||
|
* @param gids null-ok; a list of UNIX gids that the new process should
|
||
|
* setgroups() to after fork and before spawning any threads.
|
||
|
* @param runtimeFlags bit flags that enable ART features.
|
||
|
* @param rlimits null-ok an array of rlimit tuples, with the second
|
||
|
* dimension having a length of 3 and representing
|
||
|
* (resource, rlim_cur, rlim_max). These are set via the posix
|
||
|
* setrlimit(2) call.
|
||
|
* @param permittedCapabilities argument for setcap()
|
||
|
* @param effectiveCapabilities argument for setcap()
|
||
|
*
|
||
|
* @return 0 if this is the child, pid of the child
|
||
|
* if this is the parent, or -1 on error.
|
||
|
*/
|
||
|
static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
|
||
|
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
|
||
|
ZygoteHooks.preFork();
|
||
|
|
||
|
int pid = nativeForkSystemServer(
|
||
|
uid, gid, gids, runtimeFlags, rlimits,
|
||
|
permittedCapabilities, effectiveCapabilities);
|
||
|
|
||
|
// Set the Java Language thread priority to the default value for new apps.
|
||
|
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
|
||
|
|
||
|
ZygoteHooks.postForkCommon();
|
||
|
return pid;
|
||
|
}
|
||
|
|
||
|
private static native int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
|
||
|
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
|
||
|
|
||
|
/**
|
||
|
* Lets children of the zygote inherit open file descriptors to this path.
|
||
|
*/
|
||
|
protected static native void nativeAllowFileAcrossFork(String path);
|
||
|
|
||
|
/**
|
||
|
* Lets children of the zygote inherit open file descriptors that belong to the
|
||
|
* ApplicationInfo that is passed in.
|
||
|
*
|
||
|
* @param appInfo ApplicationInfo of the application
|
||
|
*/
|
||
|
static void allowAppFilesAcrossFork(ApplicationInfo appInfo) {
|
||
|
for (String path : appInfo.getAllApkPaths()) {
|
||
|
Zygote.nativeAllowFileAcrossFork(path);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Scans file descriptors in /proc/self/fd/, stores their metadata from readlink(2)/stat(2) when
|
||
|
* available. Saves this information in a global on native side, to be used by subsequent call
|
||
|
* to allowFilesOpenedByPreload(). Fatally fails if the FDs are of unsupported type and are not
|
||
|
* explicitly allowed. Ignores repeated invocations.
|
||
|
*
|
||
|
* Inspecting the FDs is more permissive than in forkAndSpecialize() because preload is invoked
|
||
|
* earlier and hence needs to allow a few open sockets. The checks in forkAndSpecialize()
|
||
|
* enforce that these sockets are closed when forking.
|
||
|
*/
|
||
|
static void markOpenedFilesBeforePreload() {
|
||
|
nativeMarkOpenedFilesBeforePreload();
|
||
|
}
|
||
|
|
||
|
private static native void nativeMarkOpenedFilesBeforePreload();
|
||
|
|
||
|
/**
|
||
|
* By scanning /proc/self/fd/ determines file descriptor numbers in this process opened since
|
||
|
* the first call to markOpenedFilesBeforePreload(). These FDs are treated as 'owned' by the
|
||
|
* custom preload of the App Zygote - the app is responsible for not sharing data with its other
|
||
|
* processes using these FDs, including by lseek(2). File descriptor types and file names are
|
||
|
* not checked. Changes in FDs recorded by markOpenedFilesBeforePreload() are not expected and
|
||
|
* kill the current process.
|
||
|
*/
|
||
|
static void allowFilesOpenedByPreload() {
|
||
|
nativeAllowFilesOpenedByPreload();
|
||
|
}
|
||
|
|
||
|
private static native void nativeAllowFilesOpenedByPreload();
|
||
|
|
||
|
/**
|
||
|
* Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
|
||
|
* @param uidGidMin The smallest allowed uid/gid
|
||
|
* @param uidGidMax The largest allowed uid/gid
|
||
|
*/
|
||
|
native protected static void nativeInstallSeccompUidGidFilter(int uidGidMin, int uidGidMax);
|
||
|
|
||
|
/**
|
||
|
* Initialize the native state of the Zygote. This inclues
|
||
|
* - Fetching socket FDs from the environment
|
||
|
* - Initializing security properties
|
||
|
* - Unmounting storage as appropriate
|
||
|
* - Loading necessary performance profile information
|
||
|
*
|
||
|
* @param isPrimary True if this is the zygote process, false if it is zygote_secondary
|
||
|
*/
|
||
|
static void initNativeState(boolean isPrimary) {
|
||
|
nativeInitNativeState(isPrimary);
|
||
|
}
|
||
|
|
||
|
protected static native void nativeInitNativeState(boolean isPrimary);
|
||
|
|
||
|
/**
|
||
|
* Returns the raw string value of a system property.
|
||
|
*
|
||
|
* Note that Device Config is not available without an application so SystemProperties is used
|
||
|
* instead.
|
||
|
*
|
||
|
* TODO (chriswailes): Cache the system property location in native code and then write a JNI
|
||
|
* function to fetch it.
|
||
|
*/
|
||
|
public static String getConfigurationProperty(String propertyName, String defaultValue) {
|
||
|
return SystemProperties.get(
|
||
|
String.join(".",
|
||
|
"persist.device_config",
|
||
|
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
|
||
|
propertyName),
|
||
|
defaultValue);
|
||
|
}
|
||
|
|
||
|
static void emptyUsapPool() {
|
||
|
nativeEmptyUsapPool();
|
||
|
}
|
||
|
|
||
|
private static native void nativeEmptyUsapPool();
|
||
|
|
||
|
/**
|
||
|
* Returns the value of a system property converted to a boolean using specific logic.
|
||
|
*
|
||
|
* Note that Device Config is not available without an application so SystemProperties is used
|
||
|
* instead.
|
||
|
*
|
||
|
* @see SystemProperties#getBoolean
|
||
|
*
|
||
|
* TODO (chriswailes): Cache the system property location in native code and then write a JNI
|
||
|
* function to fetch it.
|
||
|
* TODO (chriswailes): Move into ZygoteConfig.java once the necessary CL lands (go/ag/6580627)
|
||
|
*/
|
||
|
public static boolean getConfigurationPropertyBoolean(
|
||
|
String propertyName, Boolean defaultValue) {
|
||
|
return SystemProperties.getBoolean(
|
||
|
String.join(".",
|
||
|
"persist.device_config",
|
||
|
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
|
||
|
propertyName),
|
||
|
defaultValue);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Number of unspecialized app processes currently in the pool
|
||
|
*/
|
||
|
static int getUsapPoolCount() {
|
||
|
return nativeGetUsapPoolCount();
|
||
|
}
|
||
|
|
||
|
private static native int nativeGetUsapPoolCount();
|
||
|
|
||
|
/**
|
||
|
* @return The event FD used for communication between the signal handler and the ZygoteServer
|
||
|
* poll loop
|
||
|
*/
|
||
|
static FileDescriptor getUsapPoolEventFD() {
|
||
|
FileDescriptor fd = new FileDescriptor();
|
||
|
fd.setInt$(nativeGetUsapPoolEventFD());
|
||
|
|
||
|
return fd;
|
||
|
}
|
||
|
|
||
|
private static native int nativeGetUsapPoolEventFD();
|
||
|
|
||
|
/**
|
||
|
* Fork a new unspecialized app process from the zygote. Adds the Usap to the native
|
||
|
* Usap table.
|
||
|
*
|
||
|
* @param usapPoolSocket The server socket the USAP will call accept on
|
||
|
* @param sessionSocketRawFDs Anonymous session sockets that are currently open.
|
||
|
* These are closed in the child.
|
||
|
* @param isPriorityFork Raise the initial process priority level because this is on the
|
||
|
* critical path for application startup.
|
||
|
* @return In the child process, this returns a Runnable that waits for specialization
|
||
|
* info to start an app process. In the sygote/parent process this returns null.
|
||
|
*/
|
||
|
static @Nullable Runnable forkUsap(LocalServerSocket usapPoolSocket,
|
||
|
int[] sessionSocketRawFDs,
|
||
|
boolean isPriorityFork) {
|
||
|
FileDescriptor readFD;
|
||
|
FileDescriptor writeFD;
|
||
|
|
||
|
try {
|
||
|
FileDescriptor[] pipeFDs = Os.pipe2(O_CLOEXEC);
|
||
|
readFD = pipeFDs[0];
|
||
|
writeFD = pipeFDs[1];
|
||
|
} catch (ErrnoException errnoEx) {
|
||
|
throw new IllegalStateException("Unable to create USAP pipe.", errnoEx);
|
||
|
}
|
||
|
|
||
|
int pid = nativeForkApp(readFD.getInt$(), writeFD.getInt$(),
|
||
|
sessionSocketRawFDs, /*argsKnown=*/ false, isPriorityFork);
|
||
|
if (pid == 0) {
|
||
|
IoUtils.closeQuietly(readFD);
|
||
|
return childMain(null, usapPoolSocket, writeFD);
|
||
|
} else if (pid == -1) {
|
||
|
// Fork failed.
|
||
|
return null;
|
||
|
} else {
|
||
|
// readFD will be closed by the native code. See removeUsapTableEntry();
|
||
|
IoUtils.closeQuietly(writeFD);
|
||
|
nativeAddUsapTableEntry(pid, readFD.getInt$());
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static native int nativeForkApp(int readPipeFD,
|
||
|
int writePipeFD,
|
||
|
int[] sessionSocketRawFDs,
|
||
|
boolean argsKnown,
|
||
|
boolean isPriorityFork);
|
||
|
|
||
|
/**
|
||
|
* Add an entry for a new Usap to the table maintained in native code.
|
||
|
*/
|
||
|
@CriticalNative
|
||
|
private static native void nativeAddUsapTableEntry(int pid, int readPipeFD);
|
||
|
|
||
|
/**
|
||
|
* Fork a new app process from the zygote. argBuffer contains a fork command that
|
||
|
* request neither a child zygote, nor a wrapped process. Continue to accept connections
|
||
|
* on the specified socket, use those to refill argBuffer, and continue to process
|
||
|
* sufficiently simple fork requests. We presume that the only open file descriptors
|
||
|
* requiring special treatment are the session socket embedded in argBuffer, and
|
||
|
* zygoteSocket.
|
||
|
* @param argBuffer containing initial command and the connected socket from which to
|
||
|
* read more
|
||
|
* @param zygoteSocket socket from which to obtain new connections when current argBuffer
|
||
|
* one is disconnected
|
||
|
* @param expectedUId Uid of peer for initial requests. Subsequent requests from a different
|
||
|
* peer will cause us to return rather than perform the requested fork.
|
||
|
* @param minUid Minimum Uid enforced for all but first fork request. The caller checks
|
||
|
* the Uid policy for the initial request.
|
||
|
* @param firstNiceName name of first created process. Used for error reporting only.
|
||
|
* @return A Runnable in each child process, null in the parent.
|
||
|
* If this returns in then argBuffer still contains a command needing to be executed.
|
||
|
*/
|
||
|
static @Nullable Runnable forkSimpleApps(@NonNull ZygoteCommandBuffer argBuffer,
|
||
|
@NonNull FileDescriptor zygoteSocket,
|
||
|
int expectedUid,
|
||
|
int minUid,
|
||
|
@Nullable String firstNiceName) {
|
||
|
boolean in_child =
|
||
|
argBuffer.forkRepeatedly(zygoteSocket, expectedUid, minUid, firstNiceName);
|
||
|
if (in_child) {
|
||
|
return childMain(argBuffer, /*usapPoolSocket=*/null, /*writePipe=*/null);
|
||
|
} else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Specialize the current process into one described by argBuffer or the command read from
|
||
|
* usapPoolSocket. Exactly one of those must be null. If we are given an argBuffer, we close
|
||
|
* it. Used both for a specializing a USAP process, and for process creation without USAPs.
|
||
|
* In both cases, we specialize the process after first returning to Java code.
|
||
|
*
|
||
|
* @param writePipe The write end of the reporting pipe used to communicate with the poll loop
|
||
|
* of the ZygoteServer.
|
||
|
* @return A runnable oject representing the new application.
|
||
|
*/
|
||
|
private static Runnable childMain(@Nullable ZygoteCommandBuffer argBuffer,
|
||
|
@Nullable LocalServerSocket usapPoolSocket,
|
||
|
FileDescriptor writePipe) {
|
||
|
final int pid = Process.myPid();
|
||
|
|
||
|
DataOutputStream usapOutputStream = null;
|
||
|
ZygoteArguments args = null;
|
||
|
|
||
|
LocalSocket sessionSocket = null;
|
||
|
if (argBuffer == null) {
|
||
|
// Read arguments from usapPoolSocket instead.
|
||
|
|
||
|
Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32");
|
||
|
|
||
|
// Change the priority to max before calling accept so we can respond to new
|
||
|
// specialization requests as quickly as possible. This will be reverted to the
|
||
|
// default priority in the native specialization code.
|
||
|
boostUsapPriority();
|
||
|
|
||
|
while (true) {
|
||
|
ZygoteCommandBuffer tmpArgBuffer = null;
|
||
|
try {
|
||
|
sessionSocket = usapPoolSocket.accept();
|
||
|
// Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
|
||
|
// This is safe from a race condition because the pool is only flushed after
|
||
|
// the SystemServer changes its internal state to stop using the USAP pool.
|
||
|
blockSigTerm();
|
||
|
|
||
|
usapOutputStream =
|
||
|
new DataOutputStream(sessionSocket.getOutputStream());
|
||
|
Credentials peerCredentials = sessionSocket.getPeerCredentials();
|
||
|
tmpArgBuffer = new ZygoteCommandBuffer(sessionSocket);
|
||
|
args = ZygoteArguments.getInstance(tmpArgBuffer);
|
||
|
applyUidSecurityPolicy(args, peerCredentials);
|
||
|
// TODO (chriswailes): Should this only be run for debug builds?
|
||
|
validateUsapCommand(args);
|
||
|
break;
|
||
|
} catch (Exception ex) {
|
||
|
Log.e("USAP", ex.getMessage());
|
||
|
}
|
||
|
// Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
|
||
|
unblockSigTerm();
|
||
|
IoUtils.closeQuietly(sessionSocket);
|
||
|
IoUtils.closeQuietly(tmpArgBuffer);
|
||
|
}
|
||
|
} else {
|
||
|
// Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
|
||
|
blockSigTerm();
|
||
|
try {
|
||
|
args = ZygoteArguments.getInstance(argBuffer);
|
||
|
} catch (Exception ex) {
|
||
|
Log.e("AppStartup", ex.getMessage());
|
||
|
throw new AssertionError("Failed to parse application start command", ex);
|
||
|
}
|
||
|
// peerCredentials were checked in parent.
|
||
|
}
|
||
|
if (args == null) {
|
||
|
throw new AssertionError("Empty command line");
|
||
|
}
|
||
|
try {
|
||
|
// SIGTERM is blocked here. This prevents a USAP that is specializing from being
|
||
|
// killed during a pool flush.
|
||
|
|
||
|
applyDebuggerSystemProperty(args);
|
||
|
|
||
|
int[][] rlimits = null;
|
||
|
|
||
|
if (args.mRLimits != null) {
|
||
|
rlimits = args.mRLimits.toArray(INT_ARRAY_2D);
|
||
|
}
|
||
|
|
||
|
if (argBuffer == null) {
|
||
|
// This must happen before the SELinux policy for this process is
|
||
|
// changed when specializing.
|
||
|
try {
|
||
|
// Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
|
||
|
// Process.ProcessStartResult object.
|
||
|
usapOutputStream.writeInt(pid);
|
||
|
} catch (IOException ioEx) {
|
||
|
Log.e("USAP", "Failed to write response to session socket: "
|
||
|
+ ioEx.getMessage());
|
||
|
throw new RuntimeException(ioEx);
|
||
|
} finally {
|
||
|
try {
|
||
|
// Since the raw FD is created by init and then loaded from an environment
|
||
|
// variable (as opposed to being created by the LocalSocketImpl itself),
|
||
|
// the LocalSocket/LocalSocketImpl does not own the Os-level socket. See
|
||
|
// the spec for LocalSocket.createConnectedLocalSocket(FileDescriptor fd).
|
||
|
// Thus closing the LocalSocket does not suffice. See b/130309968 for more
|
||
|
// discussion.
|
||
|
FileDescriptor fd = usapPoolSocket.getFileDescriptor();
|
||
|
usapPoolSocket.close();
|
||
|
Os.close(fd);
|
||
|
} catch (ErrnoException | IOException ex) {
|
||
|
Log.e("USAP", "Failed to close USAP pool socket");
|
||
|
throw new RuntimeException(ex);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (writePipe != null) {
|
||
|
try {
|
||
|
ByteArrayOutputStream buffer =
|
||
|
new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
|
||
|
DataOutputStream outputStream = new DataOutputStream(buffer);
|
||
|
|
||
|
// This is written as a long so that the USAP reporting pipe and USAP pool
|
||
|
// event FD handlers in ZygoteServer.runSelectLoop can be unified. These two
|
||
|
// cases should both send/receive 8 bytes.
|
||
|
// TODO: Needs tweaking to handle the non-Usap invoke-with case, which expects
|
||
|
// a different format.
|
||
|
outputStream.writeLong(pid);
|
||
|
outputStream.flush();
|
||
|
Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
|
||
|
} catch (Exception ex) {
|
||
|
Log.e("USAP",
|
||
|
String.format("Failed to write PID (%d) to pipe (%d): %s",
|
||
|
pid, writePipe.getInt$(), ex.getMessage()));
|
||
|
throw new RuntimeException(ex);
|
||
|
} finally {
|
||
|
IoUtils.closeQuietly(writePipe);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
specializeAppProcess(args.mUid, args.mGid, args.mGids,
|
||
|
args.mRuntimeFlags, rlimits, args.mMountExternal,
|
||
|
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
|
||
|
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
|
||
|
args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
|
||
|
args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs,
|
||
|
args.mBindMountSyspropOverrides);
|
||
|
|
||
|
// While `specializeAppProcess` sets the thread name on the process's main thread, this
|
||
|
// is distinct from the app process name which appears in stack traces, as the latter is
|
||
|
// sourced from the argument buffer of the Process class. Set the app process name here.
|
||
|
Zygote.setAppProcessName(args, TAG);
|
||
|
|
||
|
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||
|
|
||
|
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
|
||
|
args.mDisabledCompatChanges,
|
||
|
args.mRemainingArgs,
|
||
|
null /* classLoader */);
|
||
|
} finally {
|
||
|
// Unblock SIGTERM to restore the process to default behavior.
|
||
|
unblockSigTerm();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void blockSigTerm() {
|
||
|
nativeBlockSigTerm();
|
||
|
}
|
||
|
|
||
|
private static native void nativeBlockSigTerm();
|
||
|
|
||
|
private static void unblockSigTerm() {
|
||
|
nativeUnblockSigTerm();
|
||
|
}
|
||
|
|
||
|
private static native void nativeUnblockSigTerm();
|
||
|
|
||
|
private static void boostUsapPriority() {
|
||
|
nativeBoostUsapPriority();
|
||
|
}
|
||
|
|
||
|
private static native void nativeBoostUsapPriority();
|
||
|
|
||
|
static void setAppProcessName(ZygoteArguments args, String loggingTag) {
|
||
|
if (args.mNiceName != null) {
|
||
|
Process.setArgV0(args.mNiceName);
|
||
|
} else if (args.mPackageName != null) {
|
||
|
Process.setArgV0(args.mPackageName);
|
||
|
} else {
|
||
|
Log.w(loggingTag, "Unable to set package name.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static final String USAP_ERROR_PREFIX = "Invalid command to USAP: ";
|
||
|
|
||
|
/**
|
||
|
* Checks a set of zygote arguments to see if they can be handled by a USAP. Throws an
|
||
|
* exception if an invalid arugment is encountered.
|
||
|
* @param args The arguments to test
|
||
|
*/
|
||
|
private static void validateUsapCommand(ZygoteArguments args) {
|
||
|
if (args.mAbiListQuery) {
|
||
|
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--query-abi-list");
|
||
|
} else if (args.mPidQuery) {
|
||
|
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--get-pid");
|
||
|
} else if (args.mPreloadDefault) {
|
||
|
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-default");
|
||
|
} else if (args.mPreloadPackage != null) {
|
||
|
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-package");
|
||
|
} else if (args.mPreloadApp != null) {
|
||
|
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--preload-app");
|
||
|
} else if (args.mStartChildZygote) {
|
||
|
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--start-child-zygote");
|
||
|
} else if (args.mApiDenylistExemptions != null) {
|
||
|
throw new IllegalArgumentException(
|
||
|
USAP_ERROR_PREFIX + "--set-api-denylist-exemptions");
|
||
|
} else if (args.mHiddenApiAccessLogSampleRate != -1) {
|
||
|
throw new IllegalArgumentException(
|
||
|
USAP_ERROR_PREFIX + "--hidden-api-log-sampling-rate=");
|
||
|
} else if (args.mHiddenApiAccessStatslogSampleRate != -1) {
|
||
|
throw new IllegalArgumentException(
|
||
|
USAP_ERROR_PREFIX + "--hidden-api-statslog-sampling-rate=");
|
||
|
} else if (args.mInvokeWith != null) {
|
||
|
throw new IllegalArgumentException(USAP_ERROR_PREFIX + "--invoke-with");
|
||
|
} else if (args.mPermittedCapabilities != 0 || args.mEffectiveCapabilities != 0) {
|
||
|
throw new ZygoteSecurityException("Client may not specify capabilities: "
|
||
|
+ "permitted=0x" + Long.toHexString(args.mPermittedCapabilities)
|
||
|
+ ", effective=0x" + Long.toHexString(args.mEffectiveCapabilities));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Raw file descriptors for the read-end of USAP reporting pipes.
|
||
|
*/
|
||
|
static int[] getUsapPipeFDs() {
|
||
|
return nativeGetUsapPipeFDs();
|
||
|
}
|
||
|
|
||
|
private static native int[] nativeGetUsapPipeFDs();
|
||
|
|
||
|
/**
|
||
|
* Remove the USAP table entry for the provided process ID.
|
||
|
*
|
||
|
* @param usapPID Process ID of the entry to remove
|
||
|
* @return True if the entry was removed; false if it doesn't exist
|
||
|
*/
|
||
|
static boolean removeUsapTableEntry(int usapPID) {
|
||
|
return nativeRemoveUsapTableEntry(usapPID);
|
||
|
}
|
||
|
|
||
|
@CriticalNative
|
||
|
private static native boolean nativeRemoveUsapTableEntry(int usapPID);
|
||
|
|
||
|
/**
|
||
|
* Return the minimum child uid that the given peer is allowed to create.
|
||
|
* uid 1000 (Process.SYSTEM_UID) may specify any uid ≥ 1000 in normal
|
||
|
* operation. It may also specify any gid and setgroups() list it chooses.
|
||
|
* In factory test mode, it may specify any UID.
|
||
|
*/
|
||
|
static int minChildUid(Credentials peer) {
|
||
|
if (peer.getUid() == Process.SYSTEM_UID
|
||
|
&& FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF) {
|
||
|
/* In normal operation, SYSTEM_UID can only specify a restricted
|
||
|
* set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
|
||
|
*/
|
||
|
return Process.SYSTEM_UID;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Adjust uid and gid arguments, ensuring that the security policy is satisfied.
|
||
|
* @param args non-null; zygote spawner arguments
|
||
|
* @param peer non-null; peer credentials
|
||
|
* @throws ZygoteSecurityException Indicates a security issue when applying the UID based
|
||
|
* security policies
|
||
|
*/
|
||
|
static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
|
||
|
throws ZygoteSecurityException {
|
||
|
|
||
|
if (args.mUidSpecified && (args.mUid < minChildUid(peer))) {
|
||
|
throw new ZygoteSecurityException(
|
||
|
"System UID may not launch process with UID < "
|
||
|
+ Process.SYSTEM_UID);
|
||
|
}
|
||
|
|
||
|
// If not otherwise specified, uid and gid are inherited from peer
|
||
|
if (!args.mUidSpecified) {
|
||
|
args.mUid = peer.getUid();
|
||
|
args.mUidSpecified = true;
|
||
|
}
|
||
|
if (!args.mGidSpecified) {
|
||
|
args.mGid = peer.getGid();
|
||
|
args.mGidSpecified = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This will enable jdwp by default for all apps. It is OK to cache this property
|
||
|
* because we expect to reboot the system whenever this property changes
|
||
|
*/
|
||
|
private static final boolean ENABLE_JDWP = SystemProperties.get(
|
||
|
"persist.debug.dalvik.vm.jdwp.enabled").equals("1");
|
||
|
|
||
|
/**
|
||
|
* This will enable ptrace by default for all apps. It is OK to cache this property
|
||
|
* because we expect to reboot the system whenever this property changes
|
||
|
*/
|
||
|
private static final boolean ENABLE_PTRACE = SystemProperties.get(
|
||
|
"persist.debug.ptrace.enabled").equals("1");
|
||
|
|
||
|
/**
|
||
|
* Applies debugger system properties to the zygote arguments.
|
||
|
*
|
||
|
* For eng builds all apps are debuggable with JDWP and ptrace.
|
||
|
*
|
||
|
* On userdebug builds if persist.debug.dalvik.vm.jdwp.enabled
|
||
|
* is 1 all apps are debuggable with JDWP and ptrace. Otherwise, the
|
||
|
* debugger state is specified via the "--enable-jdwp" flag in the
|
||
|
* spawn request.
|
||
|
*
|
||
|
* On userdebug builds if persist.debug.ptrace.enabled is 1 all
|
||
|
* apps are debuggable with ptrace.
|
||
|
*
|
||
|
* @param args non-null; zygote spawner args
|
||
|
*/
|
||
|
static void applyDebuggerSystemProperty(ZygoteArguments args) {
|
||
|
if (Build.IS_ENG || (Build.IS_USERDEBUG && ENABLE_JDWP)) {
|
||
|
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
|
||
|
// Also enable ptrace when JDWP is enabled for consistency with
|
||
|
// before persist.debug.ptrace.enabled existed.
|
||
|
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_PTRACE;
|
||
|
}
|
||
|
if (Build.IS_ENG || (Build.IS_USERDEBUG && ENABLE_PTRACE)) {
|
||
|
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_PTRACE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies zygote security policy.
|
||
|
* Based on the credentials of the process issuing a zygote command:
|
||
|
* <ol>
|
||
|
* <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
|
||
|
* wrapper command.
|
||
|
* <li> Any other uid may not specify any invoke-with argument.
|
||
|
* </ul>
|
||
|
*
|
||
|
* @param args non-null; zygote spawner arguments
|
||
|
* @param peer non-null; peer credentials
|
||
|
* @throws ZygoteSecurityException Thrown when `--invoke-with` is specified for a non-debuggable
|
||
|
* application.
|
||
|
*/
|
||
|
static void applyInvokeWithSecurityPolicy(ZygoteArguments args, Credentials peer)
|
||
|
throws ZygoteSecurityException {
|
||
|
int peerUid = peer.getUid();
|
||
|
|
||
|
if (args.mInvokeWith != null && peerUid != 0
|
||
|
&& (args.mRuntimeFlags
|
||
|
& (Zygote.DEBUG_ENABLE_JDWP | Zygote.DEBUG_ENABLE_PTRACE)) == 0) {
|
||
|
throw new ZygoteSecurityException("Peer is permitted to specify an "
|
||
|
+ "explicit invoke-with wrapper command only for debuggable "
|
||
|
+ "applications.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the wrap property if set.
|
||
|
*
|
||
|
* @param appName the application name to check
|
||
|
* @return value of wrap property or null if property not set or
|
||
|
* null if app_name is null or null if app_name is empty
|
||
|
*/
|
||
|
public static String getWrapProperty(String appName) {
|
||
|
if (appName == null || appName.isEmpty()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
String propertyValue = SystemProperties.get("wrap." + appName);
|
||
|
if (propertyValue != null && !propertyValue.isEmpty()) {
|
||
|
return propertyValue;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies invoke-with system properties to the zygote arguments.
|
||
|
*
|
||
|
* @param args non-null; zygote args
|
||
|
*/
|
||
|
static void applyInvokeWithSystemProperty(ZygoteArguments args) {
|
||
|
if (args.mInvokeWith == null) {
|
||
|
args.mInvokeWith = getWrapProperty(args.mNiceName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a managed LocalServerSocket object using a file descriptor
|
||
|
* created by an init.rc script. The init scripts that specify the
|
||
|
* sockets name can be found in system/core/rootdir. The socket is bound
|
||
|
* to the file system in the /dev/sockets/ directory, and the file
|
||
|
* descriptor is shared via the ANDROID_SOCKET_<socketName> environment
|
||
|
* variable.
|
||
|
*/
|
||
|
static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
|
||
|
int fileDesc;
|
||
|
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
|
||
|
|
||
|
try {
|
||
|
String env = System.getenv(fullSocketName);
|
||
|
fileDesc = Integer.parseInt(env);
|
||
|
} catch (RuntimeException ex) {
|
||
|
throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
FileDescriptor fd = new FileDescriptor();
|
||
|
fd.setInt$(fileDesc);
|
||
|
return new LocalServerSocket(fd);
|
||
|
} catch (IOException ex) {
|
||
|
throw new RuntimeException(
|
||
|
"Error building socket from file descriptor: " + fileDesc, ex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This function is called from native code in com_android_internal_os_Zygote.cpp
|
||
|
@SuppressWarnings("unused")
|
||
|
private static void callPostForkSystemServerHooks(int runtimeFlags) {
|
||
|
// SystemServer specific post fork hooks run before child post fork hooks.
|
||
|
ZygoteHooks.postForkSystemServer(runtimeFlags);
|
||
|
}
|
||
|
|
||
|
// This function is called from native code in com_android_internal_os_Zygote.cpp
|
||
|
@SuppressWarnings("unused")
|
||
|
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
|
||
|
boolean isZygote, String instructionSet) {
|
||
|
ZygoteHooks.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Executes "/system/bin/sh -c <command>" using the exec() system call.
|
||
|
* This method throws a runtime exception if exec() failed, otherwise, this
|
||
|
* method never returns.
|
||
|
*
|
||
|
* @param command The shell command to execute.
|
||
|
*/
|
||
|
static void execShell(String command) {
|
||
|
String[] args = { "/system/bin/sh", "-c", command };
|
||
|
try {
|
||
|
Os.execv(args[0], args);
|
||
|
} catch (ErrnoException e) {
|
||
|
throw new RuntimeException(e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Appends quotes shell arguments to the specified string builder.
|
||
|
* The arguments are quoted using single-quotes, escaped if necessary,
|
||
|
* prefixed with a space, and appended to the command.
|
||
|
*
|
||
|
* @param command A string builder for the shell command being constructed.
|
||
|
* @param args An array of argument strings to be quoted and appended to the command.
|
||
|
* @see #execShell(String)
|
||
|
*/
|
||
|
static void appendQuotedShellArgs(StringBuilder command, String[] args) {
|
||
|
for (String arg : args) {
|
||
|
command.append(" '").append(arg.replace("'", "'\\''")).append("'");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parse the given unsolicited zygote message as type SIGCHLD,
|
||
|
* extract the payload information into the given output buffer.
|
||
|
*
|
||
|
* @param in The unsolicited zygote message to be parsed
|
||
|
* @param length The number of bytes in the message
|
||
|
* @param out The output buffer where the payload information will be placed
|
||
|
* @return Number of elements being place into output buffer, or -1 if
|
||
|
* either the message is malformed or not the type as expected here.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@FastNative
|
||
|
public static native int nativeParseSigChld(byte[] in, int length, int[] out);
|
||
|
|
||
|
/**
|
||
|
* Returns whether the hardware supports memory tagging (ARM MTE).
|
||
|
*/
|
||
|
public static native boolean nativeSupportsMemoryTagging();
|
||
|
|
||
|
/**
|
||
|
* Returns whether the kernel supports tagged pointers. Present in the
|
||
|
* Android Common Kernel from 4.14 and up. By default, you should prefer
|
||
|
* fully-feature Memory Tagging, rather than the static Tagged Pointers.
|
||
|
*/
|
||
|
public static native boolean nativeSupportsTaggedPointers();
|
||
|
|
||
|
/**
|
||
|
* Returns the current native tagging level, as one of the
|
||
|
* MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or
|
||
|
* 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 <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
|
||
|
* Pointers</a>
|
||
|
*/
|
||
|
@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 <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
|
||
|
* Pointers</a>
|
||
|
*/
|
||
|
@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 <a href="https://source.android.com/devices/tech/debug/gwp-asan">GWP-ASan</a>.
|
||
|
*/
|
||
|
@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) {
|
||
|
String appOverride = SystemProperties.get("persist.arm64.memtag.app." + info.packageName);
|
||
|
if ("sync".equals(appOverride)) {
|
||
|
return MEMORY_TAG_LEVEL_SYNC;
|
||
|
} else if ("async".equals(appOverride)) {
|
||
|
return MEMORY_TAG_LEVEL_ASYNC;
|
||
|
} else if ("off".equals(appOverride)) {
|
||
|
return MEMORY_TAG_LEVEL_NONE;
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
// If we requested "sync" mode for the whole platform, upgrade mode for apps that enable
|
||
|
// MTE.
|
||
|
// This makes debugging a lot easier.
|
||
|
if (level == MEMORY_TAG_LEVEL_ASYNC
|
||
|
&& (Build.IS_USERDEBUG || Build.IS_ENG)
|
||
|
&& "sync".equals(SystemProperties.get("persist.arm64.memtag.default"))) {
|
||
|
level = MEMORY_TAG_LEVEL_SYNC;
|
||
|
}
|
||
|
|
||
|
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 (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_DEFAULT;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|