Merge "Refactored ZygoteConnection.Arguments to ZygoteArguments."
am: 3a46c1b4ce
Change-Id: Id702f32a15ad134b6d694d0fc53e1721094f78dd
This commit is contained in:
@@ -16,13 +16,23 @@
|
||||
|
||||
package com.android.internal.os;
|
||||
|
||||
import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
|
||||
|
||||
import android.net.Credentials;
|
||||
import android.os.FactoryTest;
|
||||
import android.os.IVold;
|
||||
import android.os.Process;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.Log;
|
||||
|
||||
import dalvik.system.ZygoteHooks;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
||||
/** @hide */
|
||||
public final class Zygote {
|
||||
/*
|
||||
@@ -91,6 +101,9 @@ public final class Zygote {
|
||||
*/
|
||||
public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
|
||||
|
||||
/** a prototype instance for a future List.toArray() */
|
||||
protected static final int[][] INT_ARRAY_2D = new int[0][0];
|
||||
|
||||
private Zygote() {}
|
||||
|
||||
/** Called for some security initialization before any fork. */
|
||||
@@ -231,6 +244,147 @@ public final class Zygote {
|
||||
|
||||
private static native boolean nativeRemoveBlastulaTableEntry(int blastulaPID);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param args non-null; zygote spawner arguments
|
||||
* @param peer non-null; peer credentials
|
||||
* @throws ZygoteSecurityException
|
||||
*/
|
||||
protected static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
|
||||
throws ZygoteSecurityException {
|
||||
|
||||
if (peer.getUid() == Process.SYSTEM_UID) {
|
||||
/* In normal operation, SYSTEM_UID can only specify a restricted
|
||||
* set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
|
||||
*/
|
||||
boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
|
||||
|
||||
if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies debugger system properties to the zygote arguments.
|
||||
*
|
||||
* If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
|
||||
* the debugger state is specified via the "--enable-jdwp" flag
|
||||
* in the spawn request.
|
||||
*
|
||||
* @param args non-null; zygote spawner args
|
||||
*/
|
||||
protected static void applyDebuggerSystemProperty(ZygoteArguments args) {
|
||||
if (RoSystemProperties.DEBUGGABLE) {
|
||||
args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected 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) == 0) {
|
||||
throw new ZygoteSecurityException("Peer is permitted to specify an"
|
||||
+ "explicit invoke-with wrapper command only for debuggable"
|
||||
+ "applications.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies invoke-with system properties to the zygote arguments.
|
||||
*
|
||||
* @param args non-null; zygote args
|
||||
*/
|
||||
protected static void applyInvokeWithSystemProperty(ZygoteArguments args) {
|
||||
if (args.mInvokeWith == null && args.mNiceName != null) {
|
||||
String property = "wrap." + args.mNiceName;
|
||||
args.mInvokeWith = SystemProperties.get(property);
|
||||
if (args.mInvokeWith != null && args.mInvokeWith.length() == 0) {
|
||||
args.mInvokeWith = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an argument list from the provided socket
|
||||
* @return Argument list or null if EOF is reached
|
||||
* @throws IOException passed straight through
|
||||
*/
|
||||
static String[] readArgumentList(BufferedReader socketReader) throws IOException {
|
||||
|
||||
/**
|
||||
* See android.os.Process.zygoteSendArgsAndGetPid()
|
||||
* Presently the wire format to the zygote process is:
|
||||
* a) a count of arguments (argc, in essence)
|
||||
* b) a number of newline-separated argument strings equal to count
|
||||
*
|
||||
* After the zygote process reads these it will write the pid of
|
||||
* the child or -1 on failure.
|
||||
*/
|
||||
|
||||
int argc;
|
||||
|
||||
try {
|
||||
String argc_string = socketReader.readLine();
|
||||
|
||||
if (argc_string == null) {
|
||||
// EOF reached.
|
||||
return null;
|
||||
}
|
||||
argc = Integer.parseInt(argc_string);
|
||||
|
||||
} catch (NumberFormatException ex) {
|
||||
Log.e("Zygote", "Invalid Zygote wire format: non-int at argc");
|
||||
throw new IOException("Invalid wire format");
|
||||
}
|
||||
|
||||
// See bug 1092107: large argc can be used for a DOS attack
|
||||
if (argc > MAX_ZYGOTE_ARGC) {
|
||||
throw new IOException("Max arg count exceeded");
|
||||
}
|
||||
|
||||
String[] args = new String[argc];
|
||||
for (int arg_index = 0; arg_index < argc; arg_index++) {
|
||||
args[arg_index] = socketReader.readLine();
|
||||
if (args[arg_index] == null) {
|
||||
// We got an unexpected EOF.
|
||||
throw new IOException("Truncated request");
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
private static void callPostForkSystemServerHooks() {
|
||||
// SystemServer specific post fork hooks run before child post fork hooks.
|
||||
|
||||
390
core/java/com/android/internal/os/ZygoteArguments.java
Normal file
390
core/java/com/android/internal/os/ZygoteArguments.java
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Handles argument parsing for args related to the zygote spawner.
|
||||
*
|
||||
* Current recognized args:
|
||||
* <ul>
|
||||
* <li> --setuid=<i>uid of child process, defaults to 0</i>
|
||||
* <li> --setgid=<i>gid of child process, defaults to 0</i>
|
||||
* <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
|
||||
* <li> --capabilities=<i>a pair of comma-separated integer strings
|
||||
* indicating Linux capabilities(2) set for child. The first string represents the
|
||||
* <code>permitted</code> set, and the second the
|
||||
* <code>effective</code> set. Precede each with 0 or
|
||||
* 0x for octal or hexidecimal value. If unspecified, both default to 0. This parameter is only
|
||||
* applied if the uid of the new process will be non-0. </i>
|
||||
* <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
|
||||
* <code>r</code> is the resource, <code>c</code> and <code>m</code>
|
||||
* are the settings for current and max value.</i>
|
||||
* <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
|
||||
* <li> --nice-name=<i>nice name to appear in ps</i>
|
||||
* <li> --runtime-args indicates that the remaining arg list should
|
||||
* be handed off to com.android.internal.os.RuntimeInit, rather than processed directly. Android
|
||||
* runtime startup (eg, Binder initialization) is also eschewed.
|
||||
* <li> [--] <args for RuntimeInit >
|
||||
* </ul>
|
||||
*/
|
||||
class ZygoteArguments {
|
||||
|
||||
/**
|
||||
* from --setuid
|
||||
*/
|
||||
int mUid = 0;
|
||||
boolean mUidSpecified;
|
||||
|
||||
/**
|
||||
* from --setgid
|
||||
*/
|
||||
int mGid = 0;
|
||||
boolean mGidSpecified;
|
||||
|
||||
/**
|
||||
* from --setgroups
|
||||
*/
|
||||
int[] mGids;
|
||||
|
||||
/**
|
||||
* From --runtime-flags.
|
||||
*/
|
||||
int mRuntimeFlags;
|
||||
|
||||
/**
|
||||
* From --mount-external
|
||||
*/
|
||||
int mMountExternal = Zygote.MOUNT_EXTERNAL_NONE;
|
||||
|
||||
/**
|
||||
* from --target-sdk-version.
|
||||
*/
|
||||
int mTargetSdkVersion;
|
||||
boolean mTargetSdkVersionSpecified;
|
||||
|
||||
/**
|
||||
* from --nice-name
|
||||
*/
|
||||
String mNiceName;
|
||||
|
||||
/**
|
||||
* from --capabilities
|
||||
*/
|
||||
boolean mCapabilitiesSpecified;
|
||||
long mPermittedCapabilities;
|
||||
long mEffectiveCapabilities;
|
||||
|
||||
/**
|
||||
* from --seinfo
|
||||
*/
|
||||
boolean mSeInfoSpecified;
|
||||
String mSeInfo;
|
||||
|
||||
/**
|
||||
* from all --rlimit=r,c,m
|
||||
*/
|
||||
ArrayList<int[]> mRLimits;
|
||||
|
||||
/**
|
||||
* from --invoke-with
|
||||
*/
|
||||
String mInvokeWith;
|
||||
|
||||
/**
|
||||
* Any args after and including the first non-option arg (or after a '--')
|
||||
*/
|
||||
String[] mRemainingArgs;
|
||||
|
||||
/**
|
||||
* Whether the current arguments constitute an ABI list query.
|
||||
*/
|
||||
boolean mAbiListQuery;
|
||||
|
||||
/**
|
||||
* The instruction set to use, or null when not important.
|
||||
*/
|
||||
String mInstructionSet;
|
||||
|
||||
/**
|
||||
* The app data directory. May be null, e.g., for the system server. Note that this might not be
|
||||
* reliable in the case of process-sharing apps.
|
||||
*/
|
||||
String mAppDataDir;
|
||||
|
||||
/**
|
||||
* The APK path of the package to preload, when using --preload-package.
|
||||
*/
|
||||
String mPreloadPackage;
|
||||
|
||||
/**
|
||||
* The native library path of the package to preload, when using --preload-package.
|
||||
*/
|
||||
String mPreloadPackageLibs;
|
||||
|
||||
/**
|
||||
* The filename of the native library to preload, when using --preload-package.
|
||||
*/
|
||||
String mPreloadPackageLibFileName;
|
||||
|
||||
/**
|
||||
* The cache key under which to enter the preloaded package into the classloader cache, when
|
||||
* using --preload-package.
|
||||
*/
|
||||
String mPreloadPackageCacheKey;
|
||||
|
||||
/**
|
||||
* Whether this is a request to start preloading the default resources and classes. This
|
||||
* argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started
|
||||
* with --enable-lazy-preload).
|
||||
*/
|
||||
boolean mPreloadDefault;
|
||||
|
||||
/**
|
||||
* Whether this is a request to start a zygote process as a child of this zygote. Set with
|
||||
* --start-child-zygote. The remaining arguments must include the CHILD_ZYGOTE_SOCKET_NAME_ARG
|
||||
* flag to indicate the abstract socket name that should be used for communication.
|
||||
*/
|
||||
boolean mStartChildZygote;
|
||||
|
||||
/**
|
||||
* Whether the current arguments constitute a request for the zygote's PID.
|
||||
*/
|
||||
boolean mPidQuery;
|
||||
|
||||
/**
|
||||
* Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or
|
||||
* when they change, via --set-api-blacklist-exemptions.
|
||||
*/
|
||||
String[] mApiBlacklistExemptions;
|
||||
|
||||
/**
|
||||
* Sampling rate for logging hidden API accesses to the event log. This is sent to the
|
||||
* pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
|
||||
*/
|
||||
int mHiddenApiAccessLogSampleRate = -1;
|
||||
|
||||
/**
|
||||
* Constructs instance and parses args
|
||||
*
|
||||
* @param args zygote command-line args
|
||||
*/
|
||||
ZygoteArguments(String[] args) throws IllegalArgumentException {
|
||||
parseArgs(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and
|
||||
* "--setgid=") and creates an array containing the remaining args.
|
||||
*
|
||||
* Per security review bug #1112214, duplicate args are disallowed in critical cases to make
|
||||
* injection harder.
|
||||
*/
|
||||
private void parseArgs(String[] args)
|
||||
throws IllegalArgumentException {
|
||||
int curArg = 0;
|
||||
|
||||
boolean seenRuntimeArgs = false;
|
||||
|
||||
boolean expectRuntimeArgs = true;
|
||||
for ( /* curArg */ ; curArg < args.length; curArg++) {
|
||||
String arg = args[curArg];
|
||||
|
||||
if (arg.equals("--")) {
|
||||
curArg++;
|
||||
break;
|
||||
} else if (arg.startsWith("--setuid=")) {
|
||||
if (mUidSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
mUidSpecified = true;
|
||||
mUid = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.startsWith("--setgid=")) {
|
||||
if (mGidSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
mGidSpecified = true;
|
||||
mGid = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.startsWith("--target-sdk-version=")) {
|
||||
if (mTargetSdkVersionSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate target-sdk-version specified");
|
||||
}
|
||||
mTargetSdkVersionSpecified = true;
|
||||
mTargetSdkVersion = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.equals("--runtime-args")) {
|
||||
seenRuntimeArgs = true;
|
||||
} else if (arg.startsWith("--runtime-flags=")) {
|
||||
mRuntimeFlags = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.startsWith("--seinfo=")) {
|
||||
if (mSeInfoSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
mSeInfoSpecified = true;
|
||||
mSeInfo = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.startsWith("--capabilities=")) {
|
||||
if (mCapabilitiesSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
mCapabilitiesSpecified = true;
|
||||
String capString = arg.substring(arg.indexOf('=') + 1);
|
||||
|
||||
String[] capStrings = capString.split(",", 2);
|
||||
|
||||
if (capStrings.length == 1) {
|
||||
mEffectiveCapabilities = Long.decode(capStrings[0]);
|
||||
mPermittedCapabilities = mEffectiveCapabilities;
|
||||
} else {
|
||||
mPermittedCapabilities = Long.decode(capStrings[0]);
|
||||
mEffectiveCapabilities = Long.decode(capStrings[1]);
|
||||
}
|
||||
} else if (arg.startsWith("--rlimit=")) {
|
||||
// Duplicate --rlimit arguments are specifically allowed.
|
||||
String[] limitStrings = arg.substring(arg.indexOf('=') + 1).split(",");
|
||||
|
||||
if (limitStrings.length != 3) {
|
||||
throw new IllegalArgumentException(
|
||||
"--rlimit= should have 3 comma-delimited ints");
|
||||
}
|
||||
int[] rlimitTuple = new int[limitStrings.length];
|
||||
|
||||
for (int i = 0; i < limitStrings.length; i++) {
|
||||
rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
|
||||
}
|
||||
|
||||
if (mRLimits == null) {
|
||||
mRLimits = new ArrayList();
|
||||
}
|
||||
|
||||
mRLimits.add(rlimitTuple);
|
||||
} else if (arg.startsWith("--setgroups=")) {
|
||||
if (mGids != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
|
||||
String[] params = arg.substring(arg.indexOf('=') + 1).split(",");
|
||||
|
||||
mGids = new int[params.length];
|
||||
|
||||
for (int i = params.length - 1; i >= 0; i--) {
|
||||
mGids[i] = Integer.parseInt(params[i]);
|
||||
}
|
||||
} else if (arg.equals("--invoke-with")) {
|
||||
if (mInvokeWith != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
try {
|
||||
mInvokeWith = args[++curArg];
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"--invoke-with requires argument");
|
||||
}
|
||||
} else if (arg.startsWith("--nice-name=")) {
|
||||
if (mNiceName != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
mNiceName = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.equals("--mount-external-default")) {
|
||||
mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
|
||||
} else if (arg.equals("--mount-external-read")) {
|
||||
mMountExternal = Zygote.MOUNT_EXTERNAL_READ;
|
||||
} else if (arg.equals("--mount-external-write")) {
|
||||
mMountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
|
||||
} else if (arg.equals("--query-abi-list")) {
|
||||
mAbiListQuery = true;
|
||||
} else if (arg.equals("--get-pid")) {
|
||||
mPidQuery = true;
|
||||
} else if (arg.startsWith("--instruction-set=")) {
|
||||
mInstructionSet = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.startsWith("--app-data-dir=")) {
|
||||
mAppDataDir = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.equals("--preload-package")) {
|
||||
mPreloadPackage = args[++curArg];
|
||||
mPreloadPackageLibs = args[++curArg];
|
||||
mPreloadPackageLibFileName = args[++curArg];
|
||||
mPreloadPackageCacheKey = args[++curArg];
|
||||
} else if (arg.equals("--preload-default")) {
|
||||
mPreloadDefault = true;
|
||||
expectRuntimeArgs = false;
|
||||
} else if (arg.equals("--start-child-zygote")) {
|
||||
mStartChildZygote = true;
|
||||
} else if (arg.equals("--set-api-blacklist-exemptions")) {
|
||||
// consume all remaining args; this is a stand-alone command, never included
|
||||
// with the regular fork command.
|
||||
mApiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
|
||||
curArg = args.length;
|
||||
expectRuntimeArgs = false;
|
||||
} else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
|
||||
String rateStr = arg.substring(arg.indexOf('=') + 1);
|
||||
try {
|
||||
mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid log sampling rate: " + rateStr, nfe);
|
||||
}
|
||||
expectRuntimeArgs = false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mAbiListQuery || mPidQuery) {
|
||||
if (args.length - curArg > 0) {
|
||||
throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
|
||||
}
|
||||
} else if (mPreloadPackage != null) {
|
||||
if (args.length - curArg > 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected arguments after --preload-package.");
|
||||
}
|
||||
} else if (expectRuntimeArgs) {
|
||||
if (!seenRuntimeArgs) {
|
||||
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
|
||||
}
|
||||
|
||||
mRemainingArgs = new String[args.length - curArg];
|
||||
System.arraycopy(args, curArg, mRemainingArgs, 0, mRemainingArgs.length);
|
||||
}
|
||||
|
||||
if (mStartChildZygote) {
|
||||
boolean seenChildSocketArg = false;
|
||||
for (String arg : mRemainingArgs) {
|
||||
if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
|
||||
seenChildSocketArg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!seenChildSocketArg) {
|
||||
throw new IllegalArgumentException("--start-child-zygote specified "
|
||||
+ "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,34 +22,31 @@ import static android.system.OsConstants.POLLIN;
|
||||
import static android.system.OsConstants.STDERR_FILENO;
|
||||
import static android.system.OsConstants.STDIN_FILENO;
|
||||
import static android.system.OsConstants.STDOUT_FILENO;
|
||||
|
||||
import static com.android.internal.os.ZygoteConnectionConstants.CONNECTION_TIMEOUT_MILLIS;
|
||||
import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
|
||||
import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
|
||||
|
||||
import android.net.Credentials;
|
||||
import android.net.LocalSocket;
|
||||
import android.os.FactoryTest;
|
||||
import android.os.Process;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructPollfd;
|
||||
import android.util.Log;
|
||||
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
/**
|
||||
* A connection that can make spawn requests.
|
||||
@@ -57,9 +54,6 @@ import libcore.io.IoUtils;
|
||||
class ZygoteConnection {
|
||||
private static final String TAG = "Zygote";
|
||||
|
||||
/** a prototype instance for a future List.toArray() */
|
||||
private static final int[][] intArray2d = new int[0][0];
|
||||
|
||||
/**
|
||||
* The command socket.
|
||||
*
|
||||
@@ -108,7 +102,7 @@ class ZygoteConnection {
|
||||
*
|
||||
* @return null-ok; file descriptor
|
||||
*/
|
||||
FileDescriptor getFileDesciptor() {
|
||||
FileDescriptor getFileDescriptor() {
|
||||
return mSocket.getFileDescriptor();
|
||||
}
|
||||
|
||||
@@ -122,11 +116,13 @@ class ZygoteConnection {
|
||||
*/
|
||||
Runnable processOneCommand(ZygoteServer zygoteServer) {
|
||||
String args[];
|
||||
Arguments parsedArgs = null;
|
||||
ZygoteArguments parsedArgs = null;
|
||||
FileDescriptor[] descriptors;
|
||||
|
||||
try {
|
||||
args = readArgumentList();
|
||||
args = Zygote.readArgumentList(mSocketReader);
|
||||
|
||||
// TODO (chriswailes): Remove this and add an assert.
|
||||
descriptors = mSocket.getAncillaryFileDescriptors();
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException("IOException on command socket", ex);
|
||||
@@ -143,60 +139,60 @@ class ZygoteConnection {
|
||||
FileDescriptor childPipeFd = null;
|
||||
FileDescriptor serverPipeFd = null;
|
||||
|
||||
parsedArgs = new Arguments(args);
|
||||
parsedArgs = new ZygoteArguments(args);
|
||||
|
||||
if (parsedArgs.abiListQuery) {
|
||||
if (parsedArgs.mAbiListQuery) {
|
||||
handleAbiListQuery();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parsedArgs.pidQuery) {
|
||||
if (parsedArgs.mPidQuery) {
|
||||
handlePidQuery();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parsedArgs.preloadDefault) {
|
||||
if (parsedArgs.mPreloadDefault) {
|
||||
handlePreload();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parsedArgs.preloadPackage != null) {
|
||||
handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
|
||||
parsedArgs.preloadPackageLibFileName, parsedArgs.preloadPackageCacheKey);
|
||||
if (parsedArgs.mPreloadPackage != null) {
|
||||
handlePreloadPackage(parsedArgs.mPreloadPackage, parsedArgs.mPreloadPackageLibs,
|
||||
parsedArgs.mPreloadPackageLibFileName, parsedArgs.mPreloadPackageCacheKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parsedArgs.apiBlacklistExemptions != null) {
|
||||
handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
|
||||
if (parsedArgs.mApiBlacklistExemptions != null) {
|
||||
handleApiBlacklistExemptions(parsedArgs.mApiBlacklistExemptions);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parsedArgs.hiddenApiAccessLogSampleRate != -1) {
|
||||
handleHiddenApiAccessLogSampleRate(parsedArgs.hiddenApiAccessLogSampleRate);
|
||||
if (parsedArgs.mHiddenApiAccessLogSampleRate != -1) {
|
||||
handleHiddenApiAccessLogSampleRate(parsedArgs.mHiddenApiAccessLogSampleRate);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
|
||||
throw new ZygoteSecurityException("Client may not specify capabilities: " +
|
||||
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
|
||||
", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
|
||||
if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
|
||||
throw new ZygoteSecurityException("Client may not specify capabilities: "
|
||||
+ "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
|
||||
+ ", effective=0x" + Long.toHexString(parsedArgs.mEffectiveCapabilities));
|
||||
}
|
||||
|
||||
applyUidSecurityPolicy(parsedArgs, peer);
|
||||
applyInvokeWithSecurityPolicy(parsedArgs, peer);
|
||||
Zygote.applyUidSecurityPolicy(parsedArgs, peer);
|
||||
Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
|
||||
|
||||
applyDebuggerSystemProperty(parsedArgs);
|
||||
applyInvokeWithSystemProperty(parsedArgs);
|
||||
Zygote.applyDebuggerSystemProperty(parsedArgs);
|
||||
Zygote.applyInvokeWithSystemProperty(parsedArgs);
|
||||
|
||||
int[][] rlimits = null;
|
||||
|
||||
if (parsedArgs.rlimits != null) {
|
||||
rlimits = parsedArgs.rlimits.toArray(intArray2d);
|
||||
if (parsedArgs.mRLimits != null) {
|
||||
rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
|
||||
}
|
||||
|
||||
int[] fdsToIgnore = null;
|
||||
|
||||
if (parsedArgs.invokeWith != null) {
|
||||
if (parsedArgs.mInvokeWith != null) {
|
||||
try {
|
||||
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
|
||||
childPipeFd = pipeFds[1];
|
||||
@@ -236,10 +232,10 @@ class ZygoteConnection {
|
||||
|
||||
fd = null;
|
||||
|
||||
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
|
||||
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
|
||||
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
|
||||
parsedArgs.instructionSet, parsedArgs.appDataDir);
|
||||
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
|
||||
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
|
||||
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
|
||||
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir);
|
||||
|
||||
try {
|
||||
if (pid == 0) {
|
||||
@@ -251,7 +247,7 @@ class ZygoteConnection {
|
||||
serverPipeFd = null;
|
||||
|
||||
return handleChildProc(parsedArgs, descriptors, childPipeFd,
|
||||
parsedArgs.startChildZygote);
|
||||
parsedArgs.mStartChildZygote);
|
||||
} else {
|
||||
// In the parent. A pid < 0 indicates a failure and will be handled in
|
||||
// handleParentProc.
|
||||
@@ -357,503 +353,6 @@ class ZygoteConnection {
|
||||
return isEof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles argument parsing for args related to the zygote spawner.
|
||||
*
|
||||
* Current recognized args:
|
||||
* <ul>
|
||||
* <li> --setuid=<i>uid of child process, defaults to 0</i>
|
||||
* <li> --setgid=<i>gid of child process, defaults to 0</i>
|
||||
* <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
|
||||
* <li> --capabilities=<i>a pair of comma-separated integer strings
|
||||
* indicating Linux capabilities(2) set for child. The first string
|
||||
* represents the <code>permitted</code> set, and the second the
|
||||
* <code>effective</code> set. Precede each with 0 or
|
||||
* 0x for octal or hexidecimal value. If unspecified, both default to 0.
|
||||
* This parameter is only applied if the uid of the new process will
|
||||
* be non-0. </i>
|
||||
* <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
|
||||
* <code>r</code> is the resource, <code>c</code> and <code>m</code>
|
||||
* are the settings for current and max value.</i>
|
||||
* <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
|
||||
* <li> --nice-name=<i>nice name to appear in ps</i>
|
||||
* <li> --runtime-args indicates that the remaining arg list should
|
||||
* be handed off to com.android.internal.os.RuntimeInit, rather than
|
||||
* processed directly.
|
||||
* Android runtime startup (eg, Binder initialization) is also eschewed.
|
||||
* <li> [--] <args for RuntimeInit >
|
||||
* </ul>
|
||||
*/
|
||||
static class Arguments {
|
||||
/** from --setuid */
|
||||
int uid = 0;
|
||||
boolean uidSpecified;
|
||||
|
||||
/** from --setgid */
|
||||
int gid = 0;
|
||||
boolean gidSpecified;
|
||||
|
||||
/** from --setgroups */
|
||||
int[] gids;
|
||||
|
||||
/**
|
||||
* From --runtime-flags.
|
||||
*/
|
||||
int runtimeFlags;
|
||||
|
||||
/** From --mount-external */
|
||||
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
|
||||
|
||||
/** from --target-sdk-version. */
|
||||
int targetSdkVersion;
|
||||
boolean targetSdkVersionSpecified;
|
||||
|
||||
/** from --nice-name */
|
||||
String niceName;
|
||||
|
||||
/** from --capabilities */
|
||||
boolean capabilitiesSpecified;
|
||||
long permittedCapabilities;
|
||||
long effectiveCapabilities;
|
||||
|
||||
/** from --seinfo */
|
||||
boolean seInfoSpecified;
|
||||
String seInfo;
|
||||
|
||||
/** from all --rlimit=r,c,m */
|
||||
ArrayList<int[]> rlimits;
|
||||
|
||||
/** from --invoke-with */
|
||||
String invokeWith;
|
||||
|
||||
/**
|
||||
* Any args after and including the first non-option arg
|
||||
* (or after a '--')
|
||||
*/
|
||||
String remainingArgs[];
|
||||
|
||||
/**
|
||||
* Whether the current arguments constitute an ABI list query.
|
||||
*/
|
||||
boolean abiListQuery;
|
||||
|
||||
/**
|
||||
* The instruction set to use, or null when not important.
|
||||
*/
|
||||
String instructionSet;
|
||||
|
||||
/**
|
||||
* The app data directory. May be null, e.g., for the system server. Note that this might
|
||||
* not be reliable in the case of process-sharing apps.
|
||||
*/
|
||||
String appDataDir;
|
||||
|
||||
/**
|
||||
* The APK path of the package to preload, when using --preload-package.
|
||||
*/
|
||||
String preloadPackage;
|
||||
|
||||
/**
|
||||
* The native library path of the package to preload, when using --preload-package.
|
||||
*/
|
||||
String preloadPackageLibs;
|
||||
|
||||
/**
|
||||
* The filename of the native library to preload, when using --preload-package.
|
||||
*/
|
||||
String preloadPackageLibFileName;
|
||||
|
||||
/**
|
||||
* The cache key under which to enter the preloaded package into the classloader cache,
|
||||
* when using --preload-package.
|
||||
*/
|
||||
String preloadPackageCacheKey;
|
||||
|
||||
/**
|
||||
* Whether this is a request to start preloading the default resources and classes.
|
||||
* This argument only makes sense when the zygote is in lazy preload mode (i.e, when
|
||||
* it's started with --enable-lazy-preload).
|
||||
*/
|
||||
boolean preloadDefault;
|
||||
|
||||
/**
|
||||
* Whether this is a request to start a zygote process as a child of this zygote.
|
||||
* Set with --start-child-zygote. The remaining arguments must include the
|
||||
* CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that
|
||||
* should be used for communication.
|
||||
*/
|
||||
boolean startChildZygote;
|
||||
|
||||
/**
|
||||
* Whether the current arguments constitute a request for the zygote's PID.
|
||||
*/
|
||||
boolean pidQuery;
|
||||
|
||||
/**
|
||||
* Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
|
||||
* or when they change, via --set-api-blacklist-exemptions.
|
||||
*/
|
||||
String[] apiBlacklistExemptions;
|
||||
|
||||
/**
|
||||
* Sampling rate for logging hidden API accesses to the event log. This is sent to the
|
||||
* pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
|
||||
*/
|
||||
int hiddenApiAccessLogSampleRate = -1;
|
||||
|
||||
/**
|
||||
* Constructs instance and parses args
|
||||
* @param args zygote command-line args
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
Arguments(String args[]) throws IllegalArgumentException {
|
||||
parseArgs(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the commandline arguments intended for the Zygote spawner
|
||||
* (such as "--setuid=" and "--setgid=") and creates an array
|
||||
* containing the remaining args.
|
||||
*
|
||||
* Per security review bug #1112214, duplicate args are disallowed in
|
||||
* critical cases to make injection harder.
|
||||
*/
|
||||
private void parseArgs(String args[])
|
||||
throws IllegalArgumentException {
|
||||
int curArg = 0;
|
||||
|
||||
boolean seenRuntimeArgs = false;
|
||||
|
||||
boolean expectRuntimeArgs = true;
|
||||
for ( /* curArg */ ; curArg < args.length; curArg++) {
|
||||
String arg = args[curArg];
|
||||
|
||||
if (arg.equals("--")) {
|
||||
curArg++;
|
||||
break;
|
||||
} else if (arg.startsWith("--setuid=")) {
|
||||
if (uidSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
uidSpecified = true;
|
||||
uid = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.startsWith("--setgid=")) {
|
||||
if (gidSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
gidSpecified = true;
|
||||
gid = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.startsWith("--target-sdk-version=")) {
|
||||
if (targetSdkVersionSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate target-sdk-version specified");
|
||||
}
|
||||
targetSdkVersionSpecified = true;
|
||||
targetSdkVersion = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.equals("--runtime-args")) {
|
||||
seenRuntimeArgs = true;
|
||||
} else if (arg.startsWith("--runtime-flags=")) {
|
||||
runtimeFlags = Integer.parseInt(
|
||||
arg.substring(arg.indexOf('=') + 1));
|
||||
} else if (arg.startsWith("--seinfo=")) {
|
||||
if (seInfoSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
seInfoSpecified = true;
|
||||
seInfo = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.startsWith("--capabilities=")) {
|
||||
if (capabilitiesSpecified) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
capabilitiesSpecified = true;
|
||||
String capString = arg.substring(arg.indexOf('=')+1);
|
||||
|
||||
String[] capStrings = capString.split(",", 2);
|
||||
|
||||
if (capStrings.length == 1) {
|
||||
effectiveCapabilities = Long.decode(capStrings[0]);
|
||||
permittedCapabilities = effectiveCapabilities;
|
||||
} else {
|
||||
permittedCapabilities = Long.decode(capStrings[0]);
|
||||
effectiveCapabilities = Long.decode(capStrings[1]);
|
||||
}
|
||||
} else if (arg.startsWith("--rlimit=")) {
|
||||
// Duplicate --rlimit arguments are specifically allowed.
|
||||
String[] limitStrings
|
||||
= arg.substring(arg.indexOf('=')+1).split(",");
|
||||
|
||||
if (limitStrings.length != 3) {
|
||||
throw new IllegalArgumentException(
|
||||
"--rlimit= should have 3 comma-delimited ints");
|
||||
}
|
||||
int[] rlimitTuple = new int[limitStrings.length];
|
||||
|
||||
for(int i=0; i < limitStrings.length; i++) {
|
||||
rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
|
||||
}
|
||||
|
||||
if (rlimits == null) {
|
||||
rlimits = new ArrayList();
|
||||
}
|
||||
|
||||
rlimits.add(rlimitTuple);
|
||||
} else if (arg.startsWith("--setgroups=")) {
|
||||
if (gids != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
|
||||
String[] params
|
||||
= arg.substring(arg.indexOf('=') + 1).split(",");
|
||||
|
||||
gids = new int[params.length];
|
||||
|
||||
for (int i = params.length - 1; i >= 0 ; i--) {
|
||||
gids[i] = Integer.parseInt(params[i]);
|
||||
}
|
||||
} else if (arg.equals("--invoke-with")) {
|
||||
if (invokeWith != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
try {
|
||||
invokeWith = args[++curArg];
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"--invoke-with requires argument");
|
||||
}
|
||||
} else if (arg.startsWith("--nice-name=")) {
|
||||
if (niceName != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Duplicate arg specified");
|
||||
}
|
||||
niceName = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.equals("--mount-external-default")) {
|
||||
mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
|
||||
} else if (arg.equals("--mount-external-read")) {
|
||||
mountExternal = Zygote.MOUNT_EXTERNAL_READ;
|
||||
} else if (arg.equals("--mount-external-write")) {
|
||||
mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
|
||||
} else if (arg.equals("--query-abi-list")) {
|
||||
abiListQuery = true;
|
||||
} else if (arg.equals("--get-pid")) {
|
||||
pidQuery = true;
|
||||
} else if (arg.startsWith("--instruction-set=")) {
|
||||
instructionSet = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.startsWith("--app-data-dir=")) {
|
||||
appDataDir = arg.substring(arg.indexOf('=') + 1);
|
||||
} else if (arg.equals("--preload-package")) {
|
||||
preloadPackage = args[++curArg];
|
||||
preloadPackageLibs = args[++curArg];
|
||||
preloadPackageLibFileName = args[++curArg];
|
||||
preloadPackageCacheKey = args[++curArg];
|
||||
} else if (arg.equals("--preload-default")) {
|
||||
preloadDefault = true;
|
||||
expectRuntimeArgs = false;
|
||||
} else if (arg.equals("--start-child-zygote")) {
|
||||
startChildZygote = true;
|
||||
} else if (arg.equals("--set-api-blacklist-exemptions")) {
|
||||
// consume all remaining args; this is a stand-alone command, never included
|
||||
// with the regular fork command.
|
||||
apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
|
||||
curArg = args.length;
|
||||
expectRuntimeArgs = false;
|
||||
} else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
|
||||
String rateStr = arg.substring(arg.indexOf('=') + 1);
|
||||
try {
|
||||
hiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid log sampling rate: " + rateStr, nfe);
|
||||
}
|
||||
expectRuntimeArgs = false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (abiListQuery || pidQuery) {
|
||||
if (args.length - curArg > 0) {
|
||||
throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
|
||||
}
|
||||
} else if (preloadPackage != null) {
|
||||
if (args.length - curArg > 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected arguments after --preload-package.");
|
||||
}
|
||||
} else if (expectRuntimeArgs) {
|
||||
if (!seenRuntimeArgs) {
|
||||
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
|
||||
}
|
||||
|
||||
remainingArgs = new String[args.length - curArg];
|
||||
System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
|
||||
}
|
||||
|
||||
if (startChildZygote) {
|
||||
boolean seenChildSocketArg = false;
|
||||
for (String arg : remainingArgs) {
|
||||
if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
|
||||
seenChildSocketArg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!seenChildSocketArg) {
|
||||
throw new IllegalArgumentException("--start-child-zygote specified " +
|
||||
"without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an argument list from the command socket/
|
||||
* @return Argument list or null if EOF is reached
|
||||
* @throws IOException passed straight through
|
||||
*/
|
||||
private String[] readArgumentList()
|
||||
throws IOException {
|
||||
|
||||
/**
|
||||
* See android.os.Process.zygoteSendArgsAndGetPid()
|
||||
* Presently the wire format to the zygote process is:
|
||||
* a) a count of arguments (argc, in essence)
|
||||
* b) a number of newline-separated argument strings equal to count
|
||||
*
|
||||
* After the zygote process reads these it will write the pid of
|
||||
* the child or -1 on failure.
|
||||
*/
|
||||
|
||||
int argc;
|
||||
|
||||
try {
|
||||
String s = mSocketReader.readLine();
|
||||
|
||||
if (s == null) {
|
||||
// EOF reached.
|
||||
return null;
|
||||
}
|
||||
argc = Integer.parseInt(s);
|
||||
} catch (NumberFormatException ex) {
|
||||
Log.e(TAG, "invalid Zygote wire format: non-int at argc");
|
||||
throw new IOException("invalid wire format");
|
||||
}
|
||||
|
||||
// See bug 1092107: large argc can be used for a DOS attack
|
||||
if (argc > MAX_ZYGOTE_ARGC) {
|
||||
throw new IOException("max arg count exceeded");
|
||||
}
|
||||
|
||||
String[] result = new String[argc];
|
||||
for (int i = 0; i < argc; i++) {
|
||||
result[i] = mSocketReader.readLine();
|
||||
if (result[i] == null) {
|
||||
// We got an unexpected EOF.
|
||||
throw new IOException("truncated request");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param args non-null; zygote spawner arguments
|
||||
* @param peer non-null; peer credentials
|
||||
* @throws ZygoteSecurityException
|
||||
*/
|
||||
private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
|
||||
throws ZygoteSecurityException {
|
||||
|
||||
if (peer.getUid() == Process.SYSTEM_UID) {
|
||||
/* In normal operation, SYSTEM_UID can only specify a restricted
|
||||
* set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
|
||||
*/
|
||||
boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
|
||||
|
||||
if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
|
||||
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.uidSpecified) {
|
||||
args.uid = peer.getUid();
|
||||
args.uidSpecified = true;
|
||||
}
|
||||
if (!args.gidSpecified) {
|
||||
args.gid = peer.getGid();
|
||||
args.gidSpecified = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies debugger system properties to the zygote arguments.
|
||||
*
|
||||
* If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
|
||||
* the debugger state is specified via the "--enable-jdwp" flag
|
||||
* in the spawn request.
|
||||
*
|
||||
* @param args non-null; zygote spawner args
|
||||
*/
|
||||
public static void applyDebuggerSystemProperty(Arguments args) {
|
||||
if (RoSystemProperties.DEBUGGABLE) {
|
||||
args.runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
|
||||
throws ZygoteSecurityException {
|
||||
int peerUid = peer.getUid();
|
||||
|
||||
if (args.invokeWith != null && peerUid != 0 &&
|
||||
(args.runtimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
|
||||
throw new ZygoteSecurityException("Peer is permitted to specify an"
|
||||
+ "explicit invoke-with wrapper command only for debuggable"
|
||||
+ "applications.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies invoke-with system properties to the zygote arguments.
|
||||
*
|
||||
* @param args non-null; zygote args
|
||||
*/
|
||||
public static void applyInvokeWithSystemProperty(Arguments args) {
|
||||
if (args.invokeWith == null && args.niceName != null) {
|
||||
String property = "wrap." + args.niceName;
|
||||
args.invokeWith = SystemProperties.get(property);
|
||||
if (args.invokeWith != null && args.invokeWith.length() == 0) {
|
||||
args.invokeWith = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles post-fork setup of child proc, closing sockets as appropriate,
|
||||
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
|
||||
@@ -864,7 +363,7 @@ class ZygoteConnection {
|
||||
* @param pipeFd null-ok; pipe for communication back to Zygote.
|
||||
* @param isZygote whether this new child process is itself a new Zygote.
|
||||
*/
|
||||
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
|
||||
private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors,
|
||||
FileDescriptor pipeFd, boolean isZygote) {
|
||||
/**
|
||||
* By the time we get here, the native code has closed the two actual Zygote
|
||||
@@ -887,27 +386,27 @@ class ZygoteConnection {
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedArgs.niceName != null) {
|
||||
Process.setArgV0(parsedArgs.niceName);
|
||||
if (parsedArgs.mNiceName != null) {
|
||||
Process.setArgV0(parsedArgs.mNiceName);
|
||||
}
|
||||
|
||||
// End of the postFork event.
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
if (parsedArgs.invokeWith != null) {
|
||||
WrapperInit.execApplication(parsedArgs.invokeWith,
|
||||
parsedArgs.niceName, parsedArgs.targetSdkVersion,
|
||||
if (parsedArgs.mInvokeWith != null) {
|
||||
WrapperInit.execApplication(parsedArgs.mInvokeWith,
|
||||
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
|
||||
VMRuntime.getCurrentInstructionSet(),
|
||||
pipeFd, parsedArgs.remainingArgs);
|
||||
pipeFd, parsedArgs.mRemainingArgs);
|
||||
|
||||
// Should not get here.
|
||||
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
|
||||
} else {
|
||||
if (!isZygote) {
|
||||
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
|
||||
null /* classLoader */);
|
||||
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
|
||||
parsedArgs.mRemainingArgs, null /* classLoader */);
|
||||
} else {
|
||||
return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
|
||||
parsedArgs.remainingArgs, null /* classLoader */);
|
||||
return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
|
||||
parsedArgs.mRemainingArgs, null /* classLoader */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,7 @@ import java.security.Security;
|
||||
* Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these
|
||||
* commands, forks off child processes that inherit the initial state of the VM.
|
||||
*
|
||||
* Please see {@link ZygoteConnection.Arguments} for documentation on the
|
||||
* client protocol.
|
||||
* Please see {@link ZygoteArguments} for documentation on the client protocol.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@@ -434,12 +433,12 @@ public class ZygoteInit {
|
||||
/**
|
||||
* Finish remaining work for the newly forked system server process.
|
||||
*/
|
||||
private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
|
||||
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
|
||||
// set umask to 0077 so new files and directories will default to owner-only permissions.
|
||||
Os.umask(S_IRWXG | S_IRWXO);
|
||||
|
||||
if (parsedArgs.niceName != null) {
|
||||
Process.setArgV0(parsedArgs.niceName);
|
||||
if (parsedArgs.mNiceName != null) {
|
||||
Process.setArgV0(parsedArgs.mNiceName);
|
||||
}
|
||||
|
||||
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
|
||||
@@ -458,8 +457,8 @@ public class ZygoteInit {
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedArgs.invokeWith != null) {
|
||||
String[] args = parsedArgs.remainingArgs;
|
||||
if (parsedArgs.mInvokeWith != null) {
|
||||
String[] args = parsedArgs.mRemainingArgs;
|
||||
// If we have a non-null system server class path, we'll have to duplicate the
|
||||
// existing arguments and append the classpath to it. ART will handle the classpath
|
||||
// correctly when we exec a new process.
|
||||
@@ -471,15 +470,15 @@ public class ZygoteInit {
|
||||
args = amendedArgs;
|
||||
}
|
||||
|
||||
WrapperInit.execApplication(parsedArgs.invokeWith,
|
||||
parsedArgs.niceName, parsedArgs.targetSdkVersion,
|
||||
WrapperInit.execApplication(parsedArgs.mInvokeWith,
|
||||
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
|
||||
VMRuntime.getCurrentInstructionSet(), null, args);
|
||||
|
||||
throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
|
||||
} else {
|
||||
ClassLoader cl = null;
|
||||
if (systemServerClasspath != null) {
|
||||
cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
|
||||
cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);
|
||||
|
||||
Thread.currentThread().setContextClassLoader(cl);
|
||||
}
|
||||
@@ -487,8 +486,8 @@ public class ZygoteInit {
|
||||
/*
|
||||
* Pass the remaining arguments to SystemServer.
|
||||
*/
|
||||
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
|
||||
parsedArgs.remainingArgs, cl);
|
||||
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
|
||||
parsedArgs.mRemainingArgs, cl);
|
||||
}
|
||||
|
||||
/* should never reach here */
|
||||
@@ -683,29 +682,29 @@ public class ZygoteInit {
|
||||
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
|
||||
"com.android.server.SystemServer",
|
||||
};
|
||||
ZygoteConnection.Arguments parsedArgs = null;
|
||||
ZygoteArguments parsedArgs = null;
|
||||
|
||||
int pid;
|
||||
|
||||
try {
|
||||
parsedArgs = new ZygoteConnection.Arguments(args);
|
||||
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
|
||||
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
|
||||
parsedArgs = new ZygoteArguments(args);
|
||||
Zygote.applyDebuggerSystemProperty(parsedArgs);
|
||||
Zygote.applyInvokeWithSystemProperty(parsedArgs);
|
||||
|
||||
boolean profileSystemServer = SystemProperties.getBoolean(
|
||||
"dalvik.vm.profilesystemserver", false);
|
||||
if (profileSystemServer) {
|
||||
parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
|
||||
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
|
||||
}
|
||||
|
||||
/* Request to fork the system server process */
|
||||
pid = Zygote.forkSystemServer(
|
||||
parsedArgs.uid, parsedArgs.gid,
|
||||
parsedArgs.gids,
|
||||
parsedArgs.runtimeFlags,
|
||||
parsedArgs.mUid, parsedArgs.mGid,
|
||||
parsedArgs.mGids,
|
||||
parsedArgs.mRuntimeFlags,
|
||||
null,
|
||||
parsedArgs.permittedCapabilities,
|
||||
parsedArgs.effectiveCapabilities);
|
||||
parsedArgs.mPermittedCapabilities,
|
||||
parsedArgs.mEffectiveCapabilities);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ import static android.system.OsConstants.POLLIN;
|
||||
|
||||
import android.net.LocalServerSocket;
|
||||
import android.net.LocalSocket;
|
||||
import android.system.Os;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.StructPollfd;
|
||||
import android.util.Log;
|
||||
|
||||
import android.util.Slog;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -63,8 +63,7 @@ class ZygoteServer {
|
||||
*/
|
||||
private boolean mIsForkChild;
|
||||
|
||||
ZygoteServer() {
|
||||
}
|
||||
ZygoteServer() { }
|
||||
|
||||
void setForkChild() {
|
||||
mIsForkChild = true;
|
||||
@@ -197,7 +196,7 @@ class ZygoteServer {
|
||||
if (i == 0) {
|
||||
ZygoteConnection newPeer = acceptCommandPeer(abiList);
|
||||
peers.add(newPeer);
|
||||
fds.add(newPeer.getFileDesciptor());
|
||||
fds.add(newPeer.getFileDescriptor());
|
||||
} else {
|
||||
try {
|
||||
ZygoteConnection connection = peers.get(i);
|
||||
|
||||
Reference in New Issue
Block a user