1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.os;
18
19import static android.system.OsConstants.F_SETFD;
20import static android.system.OsConstants.O_CLOEXEC;
21import static android.system.OsConstants.STDERR_FILENO;
22import static android.system.OsConstants.STDIN_FILENO;
23import static android.system.OsConstants.STDOUT_FILENO;
24
25import android.net.Credentials;
26import android.net.LocalSocket;
27import android.os.FactoryTest;
28import android.os.Process;
29import android.os.SELinux;
30import android.os.SystemProperties;
31import android.os.Trace;
32import android.system.ErrnoException;
33import android.system.Os;
34import android.util.Log;
35import dalvik.system.VMRuntime;
36import java.io.BufferedReader;
37import java.io.DataInputStream;
38import java.io.DataOutputStream;
39import java.io.FileDescriptor;
40import java.io.FileInputStream;
41import java.io.FileOutputStream;
42import java.io.IOException;
43import java.io.InputStreamReader;
44import java.io.PrintStream;
45import java.nio.charset.StandardCharsets;
46import java.util.ArrayList;
47import java.util.Arrays;
48import libcore.io.IoUtils;
49
50/**
51 * A connection that can make spawn requests.
52 */
53class ZygoteConnection {
54    private static final String TAG = "Zygote";
55
56    /** a prototype instance for a future List.toArray() */
57    private static final int[][] intArray2d = new int[0][0];
58
59    /**
60     * {@link android.net.LocalSocket#setSoTimeout} value for connections.
61     * Effectively, the amount of time a requestor has between the start of
62     * the request and the completed request. The select-loop mode Zygote
63     * doesn't have the logic to return to the select loop in the middle of
64     * a request, so we need to time out here to avoid being denial-of-serviced.
65     */
66    private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
67
68    /** max number of arguments that a connection can specify */
69    private static final int MAX_ZYGOTE_ARGC = 1024;
70
71    /**
72     * The command socket.
73     *
74     * mSocket is retained in the child process in "peer wait" mode, so
75     * that it closes when the child process terminates. In other cases,
76     * it is closed in the peer.
77     */
78    private final LocalSocket mSocket;
79    private final DataOutputStream mSocketOutStream;
80    private final BufferedReader mSocketReader;
81    private final Credentials peer;
82    private final String abiList;
83
84    /**
85     * Constructs instance from connected socket.
86     *
87     * @param socket non-null; connected socket
88     * @param abiList non-null; a list of ABIs this zygote supports.
89     * @throws IOException
90     */
91    ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
92        mSocket = socket;
93        this.abiList = abiList;
94
95        mSocketOutStream
96                = new DataOutputStream(socket.getOutputStream());
97
98        mSocketReader = new BufferedReader(
99                new InputStreamReader(socket.getInputStream()), 256);
100
101        mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
102
103        try {
104            peer = mSocket.getPeerCredentials();
105        } catch (IOException ex) {
106            Log.e(TAG, "Cannot read peer credentials", ex);
107            throw ex;
108        }
109    }
110
111    /**
112     * Returns the file descriptor of the associated socket.
113     *
114     * @return null-ok; file descriptor
115     */
116    FileDescriptor getFileDesciptor() {
117        return mSocket.getFileDescriptor();
118    }
119
120    /**
121     * Reads one start command from the command socket. If successful,
122     * a child is forked and a {@link Zygote.MethodAndArgsCaller}
123     * exception is thrown in that child while in the parent process,
124     * the method returns normally. On failure, the child is not
125     * spawned and messages are printed to the log and stderr. Returns
126     * a boolean status value indicating whether an end-of-file on the command
127     * socket has been encountered.
128     *
129     * @return false if command socket should continue to be read from, or
130     * true if an end-of-file has been encountered.
131     * @throws Zygote.MethodAndArgsCaller trampoline to invoke main()
132     * method in child process
133     */
134    boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
135
136        String args[];
137        Arguments parsedArgs = null;
138        FileDescriptor[] descriptors;
139
140        try {
141            args = readArgumentList();
142            descriptors = mSocket.getAncillaryFileDescriptors();
143        } catch (IOException ex) {
144            Log.w(TAG, "IOException on command socket " + ex.getMessage());
145            closeSocket();
146            return true;
147        }
148
149        if (args == null) {
150            // EOF reached.
151            closeSocket();
152            return true;
153        }
154
155        /** the stderr of the most recent request, if avail */
156        PrintStream newStderr = null;
157
158        if (descriptors != null && descriptors.length >= 3) {
159            newStderr = new PrintStream(
160                    new FileOutputStream(descriptors[2]));
161        }
162
163        int pid = -1;
164        FileDescriptor childPipeFd = null;
165        FileDescriptor serverPipeFd = null;
166
167        try {
168            parsedArgs = new Arguments(args);
169
170            if (parsedArgs.abiListQuery) {
171                return handleAbiListQuery();
172            }
173
174            if (parsedArgs.preloadDefault) {
175                return handlePreload();
176            }
177
178            if (parsedArgs.preloadPackage != null) {
179                return handlePreloadPackage(parsedArgs.preloadPackage,
180                        parsedArgs.preloadPackageLibs, parsedArgs.preloadPackageCacheKey);
181            }
182
183            if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
184                throw new ZygoteSecurityException("Client may not specify capabilities: " +
185                        "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
186                        ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
187            }
188
189            applyUidSecurityPolicy(parsedArgs, peer);
190            applyInvokeWithSecurityPolicy(parsedArgs, peer);
191
192            applyDebuggerSystemProperty(parsedArgs);
193            applyInvokeWithSystemProperty(parsedArgs);
194
195            int[][] rlimits = null;
196
197            if (parsedArgs.rlimits != null) {
198                rlimits = parsedArgs.rlimits.toArray(intArray2d);
199            }
200
201            int[] fdsToIgnore = null;
202
203            if (parsedArgs.invokeWith != null) {
204                FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
205                childPipeFd = pipeFds[1];
206                serverPipeFd = pipeFds[0];
207                Os.fcntlInt(childPipeFd, F_SETFD, 0);
208                fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() };
209            }
210
211            /**
212             * In order to avoid leaking descriptors to the Zygote child,
213             * the native code must close the two Zygote socket descriptors
214             * in the child process before it switches from Zygote-root to
215             * the UID and privileges of the application being launched.
216             *
217             * In order to avoid "bad file descriptor" errors when the
218             * two LocalSocket objects are closed, the Posix file
219             * descriptors are released via a dup2() call which closes
220             * the socket and substitutes an open descriptor to /dev/null.
221             */
222
223            int [] fdsToClose = { -1, -1 };
224
225            FileDescriptor fd = mSocket.getFileDescriptor();
226
227            if (fd != null) {
228                fdsToClose[0] = fd.getInt$();
229            }
230
231            fd = zygoteServer.getServerSocketFileDescriptor();
232
233            if (fd != null) {
234                fdsToClose[1] = fd.getInt$();
235            }
236
237            fd = null;
238
239            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
240                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
241                    parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
242                    parsedArgs.appDataDir);
243        } catch (ErrnoException ex) {
244            logAndPrintError(newStderr, "Exception creating pipe", ex);
245        } catch (IllegalArgumentException ex) {
246            logAndPrintError(newStderr, "Invalid zygote arguments", ex);
247        } catch (ZygoteSecurityException ex) {
248            logAndPrintError(newStderr,
249                    "Zygote security policy prevents request: ", ex);
250        }
251
252        try {
253            if (pid == 0) {
254                // in child
255                zygoteServer.closeServerSocket();
256                IoUtils.closeQuietly(serverPipeFd);
257                serverPipeFd = null;
258                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
259
260                // should never get here, the child is expected to either
261                // throw Zygote.MethodAndArgsCaller or exec().
262                return true;
263            } else {
264                // in parent...pid of < 0 means failure
265                IoUtils.closeQuietly(childPipeFd);
266                childPipeFd = null;
267                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
268            }
269        } finally {
270            IoUtils.closeQuietly(childPipeFd);
271            IoUtils.closeQuietly(serverPipeFd);
272        }
273    }
274
275    private boolean handleAbiListQuery() {
276        try {
277            final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
278            mSocketOutStream.writeInt(abiListBytes.length);
279            mSocketOutStream.write(abiListBytes);
280            return false;
281        } catch (IOException ioe) {
282            Log.e(TAG, "Error writing to command socket", ioe);
283            return true;
284        }
285    }
286
287    /**
288     * Preloads resources if the zygote is in lazily preload mode. Writes the result of the
289     * preload operation; {@code 0} when a preload was initiated due to this request and {@code 1}
290     * if no preload was initiated. The latter implies that the zygote is not configured to load
291     * resources lazy or that the zygote has already handled a previous request to handlePreload.
292     */
293    private boolean handlePreload() {
294        try {
295            if (isPreloadComplete()) {
296                mSocketOutStream.writeInt(1);
297            } else {
298                preload();
299                mSocketOutStream.writeInt(0);
300            }
301
302            return false;
303        } catch (IOException ioe) {
304            Log.e(TAG, "Error writing to command socket", ioe);
305            return true;
306        }
307    }
308
309    protected void preload() {
310        ZygoteInit.lazyPreload();
311    }
312
313    protected boolean isPreloadComplete() {
314        return ZygoteInit.isPreloadComplete();
315    }
316
317    protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
318        throw new RuntimeException("Zyogte does not support package preloading");
319    }
320
321    /**
322     * Closes socket associated with this connection.
323     */
324    void closeSocket() {
325        try {
326            mSocket.close();
327        } catch (IOException ex) {
328            Log.e(TAG, "Exception while closing command "
329                    + "socket in parent", ex);
330        }
331    }
332
333    /**
334     * Handles argument parsing for args related to the zygote spawner.
335     *
336     * Current recognized args:
337     * <ul>
338     *   <li> --setuid=<i>uid of child process, defaults to 0</i>
339     *   <li> --setgid=<i>gid of child process, defaults to 0</i>
340     *   <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
341     *   <li> --capabilities=<i>a pair of comma-separated integer strings
342     * indicating Linux capabilities(2) set for child. The first string
343     * represents the <code>permitted</code> set, and the second the
344     * <code>effective</code> set. Precede each with 0 or
345     * 0x for octal or hexidecimal value. If unspecified, both default to 0.
346     * This parameter is only applied if the uid of the new process will
347     * be non-0. </i>
348     *   <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
349     *    <code>r</code> is the resource, <code>c</code> and <code>m</code>
350     *    are the settings for current and max value.</i>
351     *   <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
352     *   <li> --nice-name=<i>nice name to appear in ps</i>
353     *   <li> --runtime-args indicates that the remaining arg list should
354     * be handed off to com.android.internal.os.RuntimeInit, rather than
355     * processed directly.
356     * Android runtime startup (eg, Binder initialization) is also eschewed.
357     *   <li> [--] &lt;args for RuntimeInit &gt;
358     * </ul>
359     */
360    static class Arguments {
361        /** from --setuid */
362        int uid = 0;
363        boolean uidSpecified;
364
365        /** from --setgid */
366        int gid = 0;
367        boolean gidSpecified;
368
369        /** from --setgroups */
370        int[] gids;
371
372        /**
373         * From --enable-jdwp, --enable-checkjni, --enable-assert,
374         * --enable-safemode, --generate-debug-info, --enable-jni-logging,
375         * --java-debuggable, and --native-debuggable.
376         */
377        int debugFlags;
378
379        /** From --mount-external */
380        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
381
382        /** from --target-sdk-version. */
383        int targetSdkVersion;
384        boolean targetSdkVersionSpecified;
385
386        /** from --nice-name */
387        String niceName;
388
389        /** from --capabilities */
390        boolean capabilitiesSpecified;
391        long permittedCapabilities;
392        long effectiveCapabilities;
393
394        /** from --seinfo */
395        boolean seInfoSpecified;
396        String seInfo;
397
398        /** from all --rlimit=r,c,m */
399        ArrayList<int[]> rlimits;
400
401        /** from --invoke-with */
402        String invokeWith;
403
404        /**
405         * Any args after and including the first non-option arg
406         * (or after a '--')
407         */
408        String remainingArgs[];
409
410        /**
411         * Whether the current arguments constitute an ABI list query.
412         */
413        boolean abiListQuery;
414
415        /**
416         * The instruction set to use, or null when not important.
417         */
418        String instructionSet;
419
420        /**
421         * The app data directory. May be null, e.g., for the system server. Note that this might
422         * not be reliable in the case of process-sharing apps.
423         */
424        String appDataDir;
425
426        /**
427         * Whether to preload a package, with the package path in the remainingArgs.
428         */
429        String preloadPackage;
430        String preloadPackageLibs;
431        String preloadPackageCacheKey;
432
433        /**
434         * Whether this is a request to start preloading the default resources and classes.
435         * This argument only makes sense when the zygote is in lazy preload mode (i.e, when
436         * it's started with --enable-lazy-preload).
437         */
438        boolean preloadDefault;
439
440        /**
441         * Constructs instance and parses args
442         * @param args zygote command-line args
443         * @throws IllegalArgumentException
444         */
445        Arguments(String args[]) throws IllegalArgumentException {
446            parseArgs(args);
447        }
448
449        /**
450         * Parses the commandline arguments intended for the Zygote spawner
451         * (such as "--setuid=" and "--setgid=") and creates an array
452         * containing the remaining args.
453         *
454         * Per security review bug #1112214, duplicate args are disallowed in
455         * critical cases to make injection harder.
456         */
457        private void parseArgs(String args[])
458                throws IllegalArgumentException {
459            int curArg = 0;
460
461            boolean seenRuntimeArgs = false;
462
463            for ( /* curArg */ ; curArg < args.length; curArg++) {
464                String arg = args[curArg];
465
466                if (arg.equals("--")) {
467                    curArg++;
468                    break;
469                } else if (arg.startsWith("--setuid=")) {
470                    if (uidSpecified) {
471                        throw new IllegalArgumentException(
472                                "Duplicate arg specified");
473                    }
474                    uidSpecified = true;
475                    uid = Integer.parseInt(
476                            arg.substring(arg.indexOf('=') + 1));
477                } else if (arg.startsWith("--setgid=")) {
478                    if (gidSpecified) {
479                        throw new IllegalArgumentException(
480                                "Duplicate arg specified");
481                    }
482                    gidSpecified = true;
483                    gid = Integer.parseInt(
484                            arg.substring(arg.indexOf('=') + 1));
485                } else if (arg.startsWith("--target-sdk-version=")) {
486                    if (targetSdkVersionSpecified) {
487                        throw new IllegalArgumentException(
488                                "Duplicate target-sdk-version specified");
489                    }
490                    targetSdkVersionSpecified = true;
491                    targetSdkVersion = Integer.parseInt(
492                            arg.substring(arg.indexOf('=') + 1));
493                } else if (arg.equals("--enable-jdwp")) {
494                    debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
495                } else if (arg.equals("--enable-safemode")) {
496                    debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
497                } else if (arg.equals("--enable-checkjni")) {
498                    debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
499                } else if (arg.equals("--generate-debug-info")) {
500                    debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
501                } else if (arg.equals("--always-jit")) {
502                    debugFlags |= Zygote.DEBUG_ALWAYS_JIT;
503                } else if (arg.equals("--native-debuggable")) {
504                    debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;
505                } else if (arg.equals("--java-debuggable")) {
506                    debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
507                } else if (arg.equals("--enable-jni-logging")) {
508                    debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
509                } else if (arg.equals("--enable-assert")) {
510                    debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
511                } else if (arg.equals("--runtime-args")) {
512                    seenRuntimeArgs = true;
513                } else if (arg.startsWith("--seinfo=")) {
514                    if (seInfoSpecified) {
515                        throw new IllegalArgumentException(
516                                "Duplicate arg specified");
517                    }
518                    seInfoSpecified = true;
519                    seInfo = arg.substring(arg.indexOf('=') + 1);
520                } else if (arg.startsWith("--capabilities=")) {
521                    if (capabilitiesSpecified) {
522                        throw new IllegalArgumentException(
523                                "Duplicate arg specified");
524                    }
525                    capabilitiesSpecified = true;
526                    String capString = arg.substring(arg.indexOf('=')+1);
527
528                    String[] capStrings = capString.split(",", 2);
529
530                    if (capStrings.length == 1) {
531                        effectiveCapabilities = Long.decode(capStrings[0]);
532                        permittedCapabilities = effectiveCapabilities;
533                    } else {
534                        permittedCapabilities = Long.decode(capStrings[0]);
535                        effectiveCapabilities = Long.decode(capStrings[1]);
536                    }
537                } else if (arg.startsWith("--rlimit=")) {
538                    // Duplicate --rlimit arguments are specifically allowed.
539                    String[] limitStrings
540                            = arg.substring(arg.indexOf('=')+1).split(",");
541
542                    if (limitStrings.length != 3) {
543                        throw new IllegalArgumentException(
544                                "--rlimit= should have 3 comma-delimited ints");
545                    }
546                    int[] rlimitTuple = new int[limitStrings.length];
547
548                    for(int i=0; i < limitStrings.length; i++) {
549                        rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
550                    }
551
552                    if (rlimits == null) {
553                        rlimits = new ArrayList();
554                    }
555
556                    rlimits.add(rlimitTuple);
557                } else if (arg.startsWith("--setgroups=")) {
558                    if (gids != null) {
559                        throw new IllegalArgumentException(
560                                "Duplicate arg specified");
561                    }
562
563                    String[] params
564                            = arg.substring(arg.indexOf('=') + 1).split(",");
565
566                    gids = new int[params.length];
567
568                    for (int i = params.length - 1; i >= 0 ; i--) {
569                        gids[i] = Integer.parseInt(params[i]);
570                    }
571                } else if (arg.equals("--invoke-with")) {
572                    if (invokeWith != null) {
573                        throw new IllegalArgumentException(
574                                "Duplicate arg specified");
575                    }
576                    try {
577                        invokeWith = args[++curArg];
578                    } catch (IndexOutOfBoundsException ex) {
579                        throw new IllegalArgumentException(
580                                "--invoke-with requires argument");
581                    }
582                } else if (arg.startsWith("--nice-name=")) {
583                    if (niceName != null) {
584                        throw new IllegalArgumentException(
585                                "Duplicate arg specified");
586                    }
587                    niceName = arg.substring(arg.indexOf('=') + 1);
588                } else if (arg.equals("--mount-external-default")) {
589                    mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
590                } else if (arg.equals("--mount-external-read")) {
591                    mountExternal = Zygote.MOUNT_EXTERNAL_READ;
592                } else if (arg.equals("--mount-external-write")) {
593                    mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
594                } else if (arg.equals("--query-abi-list")) {
595                    abiListQuery = true;
596                } else if (arg.startsWith("--instruction-set=")) {
597                    instructionSet = arg.substring(arg.indexOf('=') + 1);
598                } else if (arg.startsWith("--app-data-dir=")) {
599                    appDataDir = arg.substring(arg.indexOf('=') + 1);
600                } else if (arg.equals("--preload-package")) {
601                    preloadPackage = args[++curArg];
602                    preloadPackageLibs = args[++curArg];
603                    preloadPackageCacheKey = args[++curArg];
604                } else if (arg.equals("--preload-default")) {
605                    preloadDefault = true;
606                } else {
607                    break;
608                }
609            }
610
611            if (abiListQuery) {
612                if (args.length - curArg > 0) {
613                    throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
614                }
615            } else if (preloadPackage != null) {
616                if (args.length - curArg > 0) {
617                    throw new IllegalArgumentException(
618                            "Unexpected arguments after --preload-package.");
619                }
620            } else if (!preloadDefault) {
621                if (!seenRuntimeArgs) {
622                    throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
623                }
624
625                remainingArgs = new String[args.length - curArg];
626                System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
627            }
628        }
629    }
630
631    /**
632     * Reads an argument list from the command socket/
633     * @return Argument list or null if EOF is reached
634     * @throws IOException passed straight through
635     */
636    private String[] readArgumentList()
637            throws IOException {
638
639        /**
640         * See android.os.Process.zygoteSendArgsAndGetPid()
641         * Presently the wire format to the zygote process is:
642         * a) a count of arguments (argc, in essence)
643         * b) a number of newline-separated argument strings equal to count
644         *
645         * After the zygote process reads these it will write the pid of
646         * the child or -1 on failure.
647         */
648
649        int argc;
650
651        try {
652            String s = mSocketReader.readLine();
653
654            if (s == null) {
655                // EOF reached.
656                return null;
657            }
658            argc = Integer.parseInt(s);
659        } catch (NumberFormatException ex) {
660            Log.e(TAG, "invalid Zygote wire format: non-int at argc");
661            throw new IOException("invalid wire format");
662        }
663
664        // See bug 1092107: large argc can be used for a DOS attack
665        if (argc > MAX_ZYGOTE_ARGC) {
666            throw new IOException("max arg count exceeded");
667        }
668
669        String[] result = new String[argc];
670        for (int i = 0; i < argc; i++) {
671            result[i] = mSocketReader.readLine();
672            if (result[i] == null) {
673                // We got an unexpected EOF.
674                throw new IOException("truncated request");
675            }
676        }
677
678        return result;
679    }
680
681    /**
682     * uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
683     * operation. It may also specify any gid and setgroups() list it chooses.
684     * In factory test mode, it may specify any UID.
685     *
686     * @param args non-null; zygote spawner arguments
687     * @param peer non-null; peer credentials
688     * @throws ZygoteSecurityException
689     */
690    private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
691            throws ZygoteSecurityException {
692
693        if (peer.getUid() == Process.SYSTEM_UID) {
694            /* In normal operation, SYSTEM_UID can only specify a restricted
695             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
696             */
697            boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
698
699            if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
700                throw new ZygoteSecurityException(
701                        "System UID may not launch process with UID < "
702                        + Process.SYSTEM_UID);
703            }
704        }
705
706        // If not otherwise specified, uid and gid are inherited from peer
707        if (!args.uidSpecified) {
708            args.uid = peer.getUid();
709            args.uidSpecified = true;
710        }
711        if (!args.gidSpecified) {
712            args.gid = peer.getGid();
713            args.gidSpecified = true;
714        }
715    }
716
717    /**
718     * Applies debugger system properties to the zygote arguments.
719     *
720     * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
721     * the debugger state is specified via the "--enable-jdwp" flag
722     * in the spawn request.
723     *
724     * @param args non-null; zygote spawner args
725     */
726    public static void applyDebuggerSystemProperty(Arguments args) {
727        if (RoSystemProperties.DEBUGGABLE) {
728            args.debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
729        }
730    }
731
732    /**
733     * Applies zygote security policy.
734     * Based on the credentials of the process issuing a zygote command:
735     * <ol>
736     * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
737     * wrapper command.
738     * <li> Any other uid may not specify any invoke-with argument.
739     * </ul>
740     *
741     * @param args non-null; zygote spawner arguments
742     * @param peer non-null; peer credentials
743     * @throws ZygoteSecurityException
744     */
745    private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
746            throws ZygoteSecurityException {
747        int peerUid = peer.getUid();
748
749        if (args.invokeWith != null && peerUid != 0 &&
750            (args.debugFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
751            throw new ZygoteSecurityException("Peer is permitted to specify an"
752                    + "explicit invoke-with wrapper command only for debuggable"
753                    + "applications.");
754        }
755    }
756
757    /**
758     * Applies invoke-with system properties to the zygote arguments.
759     *
760     * @param args non-null; zygote args
761     */
762    public static void applyInvokeWithSystemProperty(Arguments args) {
763        if (args.invokeWith == null && args.niceName != null) {
764            String property = "wrap." + args.niceName;
765            args.invokeWith = SystemProperties.get(property);
766            if (args.invokeWith != null && args.invokeWith.length() == 0) {
767                args.invokeWith = null;
768            }
769        }
770    }
771
772    /**
773     * Handles post-fork setup of child proc, closing sockets as appropriate,
774     * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
775     * if successful or returning if failed.
776     *
777     * @param parsedArgs non-null; zygote args
778     * @param descriptors null-ok; new file descriptors for stdio if available.
779     * @param pipeFd null-ok; pipe for communication back to Zygote.
780     * @param newStderr null-ok; stream to use for stderr until stdio
781     * is reopened.
782     *
783     * @throws Zygote.MethodAndArgsCaller on success to
784     * trampoline to code that invokes static main.
785     */
786    private void handleChildProc(Arguments parsedArgs,
787            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
788            throws Zygote.MethodAndArgsCaller {
789        /**
790         * By the time we get here, the native code has closed the two actual Zygote
791         * socket connections, and substituted /dev/null in their place.  The LocalSocket
792         * objects still need to be closed properly.
793         */
794
795        closeSocket();
796        if (descriptors != null) {
797            try {
798                Os.dup2(descriptors[0], STDIN_FILENO);
799                Os.dup2(descriptors[1], STDOUT_FILENO);
800                Os.dup2(descriptors[2], STDERR_FILENO);
801
802                for (FileDescriptor fd: descriptors) {
803                    IoUtils.closeQuietly(fd);
804                }
805                newStderr = System.err;
806            } catch (ErrnoException ex) {
807                Log.e(TAG, "Error reopening stdio", ex);
808            }
809        }
810
811        if (parsedArgs.niceName != null) {
812            Process.setArgV0(parsedArgs.niceName);
813        }
814
815        // End of the postFork event.
816        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
817        if (parsedArgs.invokeWith != null) {
818            WrapperInit.execApplication(parsedArgs.invokeWith,
819                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
820                    VMRuntime.getCurrentInstructionSet(),
821                    pipeFd, parsedArgs.remainingArgs);
822        } else {
823            ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
824                    parsedArgs.remainingArgs, null /* classLoader */);
825        }
826    }
827
828    /**
829     * Handles post-fork cleanup of parent proc
830     *
831     * @param pid != 0; pid of child if &gt; 0 or indication of failed fork
832     * if &lt; 0;
833     * @param descriptors null-ok; file descriptors for child's new stdio if
834     * specified.
835     * @param pipeFd null-ok; pipe for communication with child.
836     * @param parsedArgs non-null; zygote args
837     * @return true for "exit command loop" and false for "continue command
838     * loop"
839     */
840    private boolean handleParentProc(int pid,
841            FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
842
843        if (pid > 0) {
844            setChildPgid(pid);
845        }
846
847        if (descriptors != null) {
848            for (FileDescriptor fd: descriptors) {
849                IoUtils.closeQuietly(fd);
850            }
851        }
852
853        boolean usingWrapper = false;
854        if (pipeFd != null && pid > 0) {
855            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
856            int innerPid = -1;
857            try {
858                innerPid = is.readInt();
859            } catch (IOException ex) {
860                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
861            } finally {
862                try {
863                    is.close();
864                } catch (IOException ex) {
865                }
866            }
867
868            // Ensure that the pid reported by the wrapped process is either the
869            // child process that we forked, or a descendant of it.
870            if (innerPid > 0) {
871                int parentPid = innerPid;
872                while (parentPid > 0 && parentPid != pid) {
873                    parentPid = Process.getParentPid(parentPid);
874                }
875                if (parentPid > 0) {
876                    Log.i(TAG, "Wrapped process has pid " + innerPid);
877                    pid = innerPid;
878                    usingWrapper = true;
879                } else {
880                    Log.w(TAG, "Wrapped process reported a pid that is not a child of "
881                            + "the process that we forked: childPid=" + pid
882                            + " innerPid=" + innerPid);
883                }
884            }
885        }
886
887        try {
888            mSocketOutStream.writeInt(pid);
889            mSocketOutStream.writeBoolean(usingWrapper);
890        } catch (IOException ex) {
891            Log.e(TAG, "Error writing to command socket", ex);
892            return true;
893        }
894
895        return false;
896    }
897
898    private void setChildPgid(int pid) {
899        // Try to move the new child into the peer's process group.
900        try {
901            Os.setpgid(pid, Os.getpgid(peer.getPid()));
902        } catch (ErrnoException ex) {
903            // This exception is expected in the case where
904            // the peer is not in our session
905            // TODO get rid of this log message in the case where
906            // getsid(0) != getsid(peer.getPid())
907            Log.i(TAG, "Zygote: setpgid failed. This is "
908                + "normal if peer is not in our session");
909        }
910    }
911
912    /**
913     * Logs an error message and prints it to the specified stream, if
914     * provided
915     *
916     * @param newStderr null-ok; a standard error stream
917     * @param message non-null; error message
918     * @param ex null-ok an exception
919     */
920    private static void logAndPrintError (PrintStream newStderr,
921            String message, Throwable ex) {
922        Log.e(TAG, message, ex);
923        if (newStderr != null) {
924            newStderr.println(message + (ex == null ? "" : ex));
925        }
926    }
927}
928