diff options
| -rw-r--r-- | core/java/com/android/internal/os/Zygote.java | 154 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteArguments.java | 390 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteConnection.java | 597 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 43 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteServer.java | 11 |
5 files changed, 618 insertions, 577 deletions
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 6e1455840247..382542a1b458 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -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. diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java new file mode 100644 index 000000000000..2e869aefcb77 --- /dev/null +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -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); + } + } + } +} diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 12761b9274e3..43f114f010bc 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -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. @@ -358,503 +354,6 @@ class ZygoteConnection { } /** - * 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 * if successful or returning if failed. @@ -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 */); } } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 37d0f3f1813e..2f00c07247db 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -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); } diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index fecf9b9da5dd..c1bfde194c4e 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -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); |