ZygoteConnection.java revision ebed7d6e35f7f960e6e6add2b8ab7c7a31a511c3
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 android.net.Credentials;
20import android.net.LocalSocket;
21import android.os.Process;
22import android.os.SystemProperties;
23import android.util.Log;
24
25import dalvik.system.PathClassLoader;
26import dalvik.system.Zygote;
27
28import java.io.BufferedReader;
29import java.io.DataInputStream;
30import java.io.DataOutputStream;
31import java.io.FileDescriptor;
32import java.io.FileInputStream;
33import java.io.FileOutputStream;
34import java.io.IOException;
35import java.io.InputStreamReader;
36import java.io.PrintStream;
37import java.util.ArrayList;
38
39import libcore.io.ErrnoException;
40import libcore.io.IoUtils;
41import libcore.io.Libcore;
42
43/**
44 * A connection that can make spawn requests.
45 */
46class ZygoteConnection {
47    private static final String TAG = "Zygote";
48
49    /** a prototype instance for a future List.toArray() */
50    private static final int[][] intArray2d = new int[0][0];
51
52    /**
53     * {@link android.net.LocalSocket#setSoTimeout} value for connections.
54     * Effectively, the amount of time a requestor has between the start of
55     * the request and the completed request. The select-loop mode Zygote
56     * doesn't have the logic to return to the select loop in the middle of
57     * a request, so we need to time out here to avoid being denial-of-serviced.
58     */
59    private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
60
61    /** max number of arguments that a connection can specify */
62    private static final int MAX_ZYGOTE_ARGC=1024;
63
64    /**
65     * The command socket.
66     *
67     * mSocket is retained in the child process in "peer wait" mode, so
68     * that it closes when the child process terminates. In other cases,
69     * it is closed in the peer.
70     */
71    private final LocalSocket mSocket;
72    private final DataOutputStream mSocketOutStream;
73    private final BufferedReader mSocketReader;
74    private final Credentials peer;
75
76    /**
77     * A long-lived reference to the original command socket used to launch
78     * this peer. If "peer wait" mode is specified, the process that requested
79     * the new VM instance intends to track the lifetime of the spawned instance
80     * via the command socket. In this case, the command socket is closed
81     * in the Zygote and placed here in the spawned instance so that it will
82     * not be collected and finalized. This field remains null at all times
83     * in the original Zygote process, and in all spawned processes where
84     * "peer-wait" mode was not requested.
85     */
86    private static LocalSocket sPeerWaitSocket = null;
87
88    /**
89     * Constructs instance from connected socket.
90     *
91     * @param socket non-null; connected socket
92     * @throws IOException
93     */
94    ZygoteConnection(LocalSocket socket) throws IOException {
95        mSocket = socket;
96
97        mSocketOutStream
98                = new DataOutputStream(socket.getOutputStream());
99
100        mSocketReader = new BufferedReader(
101                new InputStreamReader(socket.getInputStream()), 256);
102
103        mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
104
105        try {
106            peer = mSocket.getPeerCredentials();
107        } catch (IOException ex) {
108            Log.e(TAG, "Cannot read peer credentials", ex);
109            throw ex;
110        }
111    }
112
113    /**
114     * Returns the file descriptor of the associated socket.
115     *
116     * @return null-ok; file descriptor
117     */
118    FileDescriptor getFileDesciptor() {
119        return mSocket.getFileDescriptor();
120    }
121
122    /**
123     * Reads start commands from an open command socket.
124     * Start commands are presently a pair of newline-delimited lines
125     * indicating a) class to invoke main() on b) nice name to set argv[0] to.
126     * Continues to read commands and forkAndSpecialize children until
127     * the socket is closed. This method is used in ZYGOTE_FORK_MODE
128     *
129     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
130     * method in child process
131     */
132    void run() throws ZygoteInit.MethodAndArgsCaller {
133
134        int loopCount = ZygoteInit.GC_LOOP_COUNT;
135
136        while (true) {
137            /*
138             * Call gc() before we block in readArgumentList().
139             * It's work that has to be done anyway, and it's better
140             * to avoid making every child do it.  It will also
141             * madvise() any free memory as a side-effect.
142             *
143             * Don't call it every time, because walking the entire
144             * heap is a lot of overhead to free a few hundred bytes.
145             */
146            if (loopCount <= 0) {
147                ZygoteInit.gc();
148                loopCount = ZygoteInit.GC_LOOP_COUNT;
149            } else {
150                loopCount--;
151            }
152
153            if (runOnce()) {
154                break;
155            }
156        }
157    }
158
159    /**
160     * Reads one start command from the command socket. If successful,
161     * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
162     * exception is thrown in that child while in the parent process,
163     * the method returns normally. On failure, the child is not
164     * spawned and messages are printed to the log and stderr. Returns
165     * a boolean status value indicating whether an end-of-file on the command
166     * socket has been encountered.
167     *
168     * @return false if command socket should continue to be read from, or
169     * true if an end-of-file has been encountered.
170     * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
171     * method in child process
172     */
173    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
174
175        String args[];
176        Arguments parsedArgs = null;
177        FileDescriptor[] descriptors;
178
179        try {
180            args = readArgumentList();
181            descriptors = mSocket.getAncillaryFileDescriptors();
182        } catch (IOException ex) {
183            Log.w(TAG, "IOException on command socket " + ex.getMessage());
184            closeSocket();
185            return true;
186        }
187
188        if (args == null) {
189            // EOF reached.
190            closeSocket();
191            return true;
192        }
193
194        /** the stderr of the most recent request, if avail */
195        PrintStream newStderr = null;
196
197        if (descriptors != null && descriptors.length >= 3) {
198            newStderr = new PrintStream(
199                    new FileOutputStream(descriptors[2]));
200        }
201
202        int pid = -1;
203        FileDescriptor childPipeFd = null;
204        FileDescriptor serverPipeFd = null;
205
206        try {
207            parsedArgs = new Arguments(args);
208
209            applyUidSecurityPolicy(parsedArgs, peer);
210            applyRlimitSecurityPolicy(parsedArgs, peer);
211            applyCapabilitiesSecurityPolicy(parsedArgs, peer);
212            applyInvokeWithSecurityPolicy(parsedArgs, peer);
213
214            applyDebuggerSystemProperty(parsedArgs);
215            applyInvokeWithSystemProperty(parsedArgs);
216
217            int[][] rlimits = null;
218
219            if (parsedArgs.rlimits != null) {
220                rlimits = parsedArgs.rlimits.toArray(intArray2d);
221            }
222
223            if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
224                FileDescriptor[] pipeFds = Libcore.os.pipe();
225                childPipeFd = pipeFds[1];
226                serverPipeFd = pipeFds[0];
227                ZygoteInit.setCloseOnExec(serverPipeFd, true);
228            }
229
230            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
231                    parsedArgs.gids, parsedArgs.debugFlags, rlimits);
232        } catch (IOException ex) {
233            logAndPrintError(newStderr, "Exception creating pipe", ex);
234        } catch (ErrnoException ex) {
235            logAndPrintError(newStderr, "Exception creating pipe", ex);
236        } catch (IllegalArgumentException ex) {
237            logAndPrintError(newStderr, "Invalid zygote arguments", ex);
238        } catch (ZygoteSecurityException ex) {
239            logAndPrintError(newStderr,
240                    "Zygote security policy prevents request: ", ex);
241        }
242
243        try {
244            if (pid == 0) {
245                // in child
246                IoUtils.closeQuietly(serverPipeFd);
247                serverPipeFd = null;
248                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
249
250                // should never get here, the child is expected to either
251                // throw ZygoteInit.MethodAndArgsCaller or exec().
252                return true;
253            } else {
254                // in parent...pid of < 0 means failure
255                IoUtils.closeQuietly(childPipeFd);
256                childPipeFd = null;
257                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
258            }
259        } finally {
260            IoUtils.closeQuietly(childPipeFd);
261            IoUtils.closeQuietly(serverPipeFd);
262        }
263    }
264
265    /**
266     * Closes socket associated with this connection.
267     */
268    void closeSocket() {
269        try {
270            mSocket.close();
271        } catch (IOException ex) {
272            Log.e(TAG, "Exception while closing command "
273                    + "socket in parent", ex);
274        }
275    }
276
277    /**
278     * Handles argument parsing for args related to the zygote spawner.
279     *
280     * Current recognized args:
281     * <ul>
282     *   <li> --setuid=<i>uid of child process, defaults to 0</i>
283     *   <li> --setgid=<i>gid of child process, defaults to 0</i>
284     *   <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
285     *   <li> --capabilities=<i>a pair of comma-separated integer strings
286     * indicating Linux capabilities(2) set for child. The first string
287     * represents the <code>permitted</code> set, and the second the
288     * <code>effective</code> set. Precede each with 0 or
289     * 0x for octal or hexidecimal value. If unspecified, both default to 0.
290     * This parameter is only applied if the uid of the new process will
291     * be non-0. </i>
292     *   <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
293     *    <code>r</code> is the resource, <code>c</code> and <code>m</code>
294     *    are the settings for current and max value.</i>
295     *   <li> --peer-wait indicates that the command socket should
296     * be inherited by (and set to close-on-exec in) the spawned process
297     * and used to track the lifetime of that process. The spawning process
298     * then exits. Without this flag, it is retained by the spawning process
299     * (and closed in the child) in expectation of a new spawn request.
300     *   <li> --classpath=<i>colon-separated classpath</i> indicates
301     * that the specified class (which must b first non-flag argument) should
302     * be loaded from jar files in the specified classpath. Incompatible with
303     * --runtime-init
304     *   <li> --runtime-init indicates that the remaining arg list should
305     * be handed off to com.android.internal.os.RuntimeInit, rather than
306     * processed directly
307     * Android runtime startup (eg, Binder initialization) is also eschewed.
308     *   <li> --nice-name=<i>nice name to appear in ps</i>
309     *   <li> If <code>--runtime-init</code> is present:
310     *      [--] &lt;args for RuntimeInit &gt;
311     *   <li> If <code>--runtime-init</code> is absent:
312     *      [--] &lt;classname&gt; [args...]
313     * </ul>
314     */
315    static class Arguments {
316        /** from --setuid */
317        int uid = 0;
318        boolean uidSpecified;
319
320        /** from --setgid */
321        int gid = 0;
322        boolean gidSpecified;
323
324        /** from --setgroups */
325        int[] gids;
326
327        /** from --peer-wait */
328        boolean peerWait;
329
330        /**
331         * From --enable-debugger, --enable-checkjni, --enable-assert, and
332         * --enable-safemode
333         */
334        int debugFlags;
335
336        /** from --classpath */
337        String classpath;
338
339        /** from --runtime-init */
340        boolean runtimeInit;
341
342        /** from --nice-name */
343        String niceName;
344
345        /** from --capabilities */
346        boolean capabilitiesSpecified;
347        long permittedCapabilities;
348        long effectiveCapabilities;
349
350        /** from all --rlimit=r,c,m */
351        ArrayList<int[]> rlimits;
352
353        /** from --invoke-with */
354        String invokeWith;
355
356        /**
357         * Any args after and including the first non-option arg
358         * (or after a '--')
359         */
360        String remainingArgs[];
361
362        /**
363         * Constructs instance and parses args
364         * @param args zygote command-line args
365         * @throws IllegalArgumentException
366         */
367        Arguments(String args[]) throws IllegalArgumentException {
368            parseArgs(args);
369        }
370
371        /**
372         * Parses the commandline arguments intended for the Zygote spawner
373         * (such as "--setuid=" and "--setgid=") and creates an array
374         * containing the remaining args.
375         *
376         * Per security review bug #1112214, duplicate args are disallowed in
377         * critical cases to make injection harder.
378         */
379        private void parseArgs(String args[])
380                throws IllegalArgumentException {
381            int curArg = 0;
382
383            for ( /* curArg */ ; curArg < args.length; curArg++) {
384                String arg = args[curArg];
385
386                if (arg.equals("--")) {
387                    curArg++;
388                    break;
389                } else if (arg.startsWith("--setuid=")) {
390                    if (uidSpecified) {
391                        throw new IllegalArgumentException(
392                                "Duplicate arg specified");
393                    }
394                    uidSpecified = true;
395                    uid = Integer.parseInt(
396                            arg.substring(arg.indexOf('=') + 1));
397                } else if (arg.startsWith("--setgid=")) {
398                    if (gidSpecified) {
399                        throw new IllegalArgumentException(
400                                "Duplicate arg specified");
401                    }
402                    gidSpecified = true;
403                    gid = Integer.parseInt(
404                            arg.substring(arg.indexOf('=') + 1));
405                } else if (arg.equals("--enable-debugger")) {
406                    debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
407                } else if (arg.equals("--enable-safemode")) {
408                    debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
409                } else if (arg.equals("--enable-checkjni")) {
410                    debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
411                } else if (arg.equals("--enable-assert")) {
412                    debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
413                } else if (arg.equals("--peer-wait")) {
414                    peerWait = true;
415                } else if (arg.equals("--runtime-init")) {
416                    runtimeInit = true;
417                } else if (arg.startsWith("--capabilities=")) {
418                    if (capabilitiesSpecified) {
419                        throw new IllegalArgumentException(
420                                "Duplicate arg specified");
421                    }
422                    capabilitiesSpecified = true;
423                    String capString = arg.substring(arg.indexOf('=')+1);
424
425                    String[] capStrings = capString.split(",", 2);
426
427                    if (capStrings.length == 1) {
428                        effectiveCapabilities = Long.decode(capStrings[0]);
429                        permittedCapabilities = effectiveCapabilities;
430                    } else {
431                        permittedCapabilities = Long.decode(capStrings[0]);
432                        effectiveCapabilities = Long.decode(capStrings[1]);
433                    }
434                } else if (arg.startsWith("--rlimit=")) {
435                    // Duplicate --rlimit arguments are specifically allowed.
436                    String[] limitStrings
437                            = arg.substring(arg.indexOf('=')+1).split(",");
438
439                    if (limitStrings.length != 3) {
440                        throw new IllegalArgumentException(
441                                "--rlimit= should have 3 comma-delimited ints");
442                    }
443                    int[] rlimitTuple = new int[limitStrings.length];
444
445                    for(int i=0; i < limitStrings.length; i++) {
446                        rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
447                    }
448
449                    if (rlimits == null) {
450                        rlimits = new ArrayList();
451                    }
452
453                    rlimits.add(rlimitTuple);
454                } else if (arg.equals("-classpath")) {
455                    if (classpath != null) {
456                        throw new IllegalArgumentException(
457                                "Duplicate arg specified");
458                    }
459                    try {
460                        classpath = args[++curArg];
461                    } catch (IndexOutOfBoundsException ex) {
462                        throw new IllegalArgumentException(
463                                "-classpath requires argument");
464                    }
465                } else if (arg.startsWith("--setgroups=")) {
466                    if (gids != null) {
467                        throw new IllegalArgumentException(
468                                "Duplicate arg specified");
469                    }
470
471                    String[] params
472                            = arg.substring(arg.indexOf('=') + 1).split(",");
473
474                    gids = new int[params.length];
475
476                    for (int i = params.length - 1; i >= 0 ; i--) {
477                        gids[i] = Integer.parseInt(params[i]);
478                    }
479                } else if (arg.equals("--invoke-with")) {
480                    if (invokeWith != null) {
481                        throw new IllegalArgumentException(
482                                "Duplicate arg specified");
483                    }
484                    try {
485                        invokeWith = args[++curArg];
486                    } catch (IndexOutOfBoundsException ex) {
487                        throw new IllegalArgumentException(
488                                "--invoke-with requires argument");
489                    }
490                } else if (arg.startsWith("--nice-name=")) {
491                    if (niceName != null) {
492                        throw new IllegalArgumentException(
493                                "Duplicate arg specified");
494                    }
495                    niceName = arg.substring(arg.indexOf('=') + 1);
496                } else {
497                    break;
498                }
499            }
500
501            if (runtimeInit && classpath != null) {
502                throw new IllegalArgumentException(
503                        "--runtime-init and -classpath are incompatible");
504            }
505
506            remainingArgs = new String[args.length - curArg];
507
508            System.arraycopy(args, curArg, remainingArgs, 0,
509                    remainingArgs.length);
510        }
511    }
512
513    /**
514     * Reads an argument list from the command socket/
515     * @return Argument list or null if EOF is reached
516     * @throws IOException passed straight through
517     */
518    private String[] readArgumentList()
519            throws IOException {
520
521        /**
522         * See android.os.Process.zygoteSendArgsAndGetPid()
523         * Presently the wire format to the zygote process is:
524         * a) a count of arguments (argc, in essence)
525         * b) a number of newline-separated argument strings equal to count
526         *
527         * After the zygote process reads these it will write the pid of
528         * the child or -1 on failure.
529         */
530
531        int argc;
532
533        try {
534            String s = mSocketReader.readLine();
535
536            if (s == null) {
537                // EOF reached.
538                return null;
539            }
540            argc = Integer.parseInt(s);
541        } catch (NumberFormatException ex) {
542            Log.e(TAG, "invalid Zygote wire format: non-int at argc");
543            throw new IOException("invalid wire format");
544        }
545
546        // See bug 1092107: large argc can be used for a DOS attack
547        if (argc > MAX_ZYGOTE_ARGC) {
548            throw new IOException("max arg count exceeded");
549        }
550
551        String[] result = new String[argc];
552        for (int i = 0; i < argc; i++) {
553            result[i] = mSocketReader.readLine();
554            if (result[i] == null) {
555                // We got an unexpected EOF.
556                throw new IOException("truncated request");
557            }
558        }
559
560        return result;
561    }
562
563    /**
564     * Applies zygote security policy per bugs #875058 and #1082165.
565     * Based on the credentials of the process issuing a zygote command:
566     * <ol>
567     * <li> uid 0 (root) may specify any uid, gid, and setgroups() list
568     * <li> uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
569     * operation. It may also specify any gid and setgroups() list it chooses.
570     * In factory test mode, it may specify any UID.
571     * <li> Any other uid may not specify any uid, gid, or setgroups list. The
572     * uid and gid will be inherited from the requesting process.
573     * </ul>
574     *
575     * @param args non-null; zygote spawner arguments
576     * @param peer non-null; peer credentials
577     * @throws ZygoteSecurityException
578     */
579    private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
580            throws ZygoteSecurityException {
581
582        int peerUid = peer.getUid();
583
584        if (peerUid == 0) {
585            // Root can do what it wants
586        } else if (peerUid == Process.SYSTEM_UID ) {
587            // System UID is restricted, except in factory test mode
588            String factoryTest = SystemProperties.get("ro.factorytest");
589            boolean uidRestricted;
590
591            /* In normal operation, SYSTEM_UID can only specify a restricted
592             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
593             */
594            uidRestricted
595                 = !(factoryTest.equals("1") || factoryTest.equals("2"));
596
597            if (uidRestricted
598                    && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
599                throw new ZygoteSecurityException(
600                        "System UID may not launch process with UID < "
601                                + Process.SYSTEM_UID);
602            }
603        } else {
604            // Everything else
605            if (args.uidSpecified || args.gidSpecified
606                || args.gids != null) {
607                throw new ZygoteSecurityException(
608                        "App UIDs may not specify uid's or gid's");
609            }
610        }
611
612        // If not otherwise specified, uid and gid are inherited from peer
613        if (!args.uidSpecified) {
614            args.uid = peer.getUid();
615            args.uidSpecified = true;
616        }
617        if (!args.gidSpecified) {
618            args.gid = peer.getGid();
619            args.gidSpecified = true;
620        }
621    }
622
623
624    /**
625     * Applies debugger system properties to the zygote arguments.
626     *
627     * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
628     * the debugger state is specified via the "--enable-debugger" flag
629     * in the spawn request.
630     *
631     * @param args non-null; zygote spawner args
632     */
633    public static void applyDebuggerSystemProperty(Arguments args) {
634        if ("1".equals(SystemProperties.get("ro.debuggable"))) {
635            args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
636        }
637    }
638
639    /**
640     * Applies zygote security policy per bug #1042973. Based on the credentials
641     * of the process issuing a zygote command:
642     * <ol>
643     * <li> peers of  uid 0 (root) and uid 1000 (Process.SYSTEM_UID)
644     * may specify any rlimits.
645     * <li> All other uids may not specify rlimits.
646     * </ul>
647     * @param args non-null; zygote spawner arguments
648     * @param peer non-null; peer credentials
649     * @throws ZygoteSecurityException
650     */
651    private static void applyRlimitSecurityPolicy(
652            Arguments args, Credentials peer)
653            throws ZygoteSecurityException {
654
655        int peerUid = peer.getUid();
656
657        if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) {
658            // All peers with UID other than root or SYSTEM_UID
659            if (args.rlimits != null) {
660                throw new ZygoteSecurityException(
661                        "This UID may not specify rlimits.");
662            }
663        }
664    }
665
666    /**
667     * Applies zygote security policy per bug #1042973. A root peer may
668     * spawn an instance with any capabilities. All other uids may spawn
669     * instances with any of the capabilities in the peer's permitted set
670     * but no more.
671     *
672     * @param args non-null; zygote spawner arguments
673     * @param peer non-null; peer credentials
674     * @throws ZygoteSecurityException
675     */
676    private static void applyCapabilitiesSecurityPolicy(
677            Arguments args, Credentials peer)
678            throws ZygoteSecurityException {
679
680        if (args.permittedCapabilities == 0
681                && args.effectiveCapabilities == 0) {
682            // nothing to check
683            return;
684        }
685
686        if (peer.getUid() == 0) {
687            // root may specify anything
688            return;
689        }
690
691        long permittedCaps;
692
693        try {
694            permittedCaps = ZygoteInit.capgetPermitted(peer.getPid());
695        } catch (IOException ex) {
696            throw new ZygoteSecurityException(
697                    "Error retrieving peer's capabilities.");
698        }
699
700        /*
701         * Ensure that the client did not specify an effective set larger
702         * than the permitted set. The kernel will enforce this too, but we
703         * do it here to make the following check easier.
704         */
705        if (((~args.permittedCapabilities) & args.effectiveCapabilities) != 0) {
706            throw new ZygoteSecurityException(
707                    "Effective capabilities cannot be superset of "
708                            + " permitted capabilities" );
709        }
710
711        /*
712         * Ensure that the new permitted (and thus the new effective) set is
713         * a subset of the peer process's permitted set
714         */
715
716        if (((~permittedCaps) & args.permittedCapabilities) != 0) {
717            throw new ZygoteSecurityException(
718                    "Peer specified unpermitted capabilities" );
719        }
720    }
721
722    /**
723     * Applies zygote security policy.
724     * Based on the credentials of the process issuing a zygote command:
725     * <ol>
726     * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
727     * wrapper command.
728     * <li> Any other uid may not specify any invoke-with argument.
729     * </ul>
730     *
731     * @param args non-null; zygote spawner arguments
732     * @param peer non-null; peer credentials
733     * @throws ZygoteSecurityException
734     */
735    private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
736            throws ZygoteSecurityException {
737        int peerUid = peer.getUid();
738
739        if (args.invokeWith != null && peerUid != 0) {
740            throw new ZygoteSecurityException("Peer is not permitted to specify "
741                    + "an explicit invoke-with wrapper command");
742        }
743    }
744
745    /**
746     * Applies invoke-with system properties to the zygote arguments.
747     *
748     * @param parsedArgs non-null; zygote args
749     */
750    public static void applyInvokeWithSystemProperty(Arguments args) {
751        if (args.invokeWith == null && args.niceName != null) {
752            if (args.niceName != null) {
753                String property = "wrap." + args.niceName;
754                if (property.length() > 31) {
755                    property = property.substring(0, 31);
756                }
757                args.invokeWith = SystemProperties.get(property);
758                if (args.invokeWith != null && args.invokeWith.length() == 0) {
759                    args.invokeWith = null;
760                }
761            }
762        }
763    }
764
765    /**
766     * Handles post-fork setup of child proc, closing sockets as appropriate,
767     * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
768     * if successful or returning if failed.
769     *
770     * @param parsedArgs non-null; zygote args
771     * @param descriptors null-ok; new file descriptors for stdio if available.
772     * @param pipeFd null-ok; pipe for communication back to Zygote.
773     * @param newStderr null-ok; stream to use for stderr until stdio
774     * is reopened.
775     *
776     * @throws ZygoteInit.MethodAndArgsCaller on success to
777     * trampoline to code that invokes static main.
778     */
779    private void handleChildProc(Arguments parsedArgs,
780            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
781            throws ZygoteInit.MethodAndArgsCaller {
782
783        /*
784         * Close the socket, unless we're in "peer wait" mode, in which
785         * case it's used to track the liveness of this process.
786         */
787
788        if (parsedArgs.peerWait) {
789            try {
790                ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true);
791                sPeerWaitSocket = mSocket;
792            } catch (IOException ex) {
793                Log.e(TAG, "Zygote Child: error setting peer wait "
794                        + "socket to be close-on-exec", ex);
795            }
796        } else {
797            closeSocket();
798            ZygoteInit.closeServerSocket();
799        }
800
801        if (descriptors != null) {
802            try {
803                ZygoteInit.reopenStdio(descriptors[0],
804                        descriptors[1], descriptors[2]);
805
806                for (FileDescriptor fd: descriptors) {
807                    IoUtils.closeQuietly(fd);
808                }
809                newStderr = System.err;
810            } catch (IOException ex) {
811                Log.e(TAG, "Error reopening stdio", ex);
812            }
813        }
814
815        if (parsedArgs.niceName != null) {
816            Process.setArgV0(parsedArgs.niceName);
817        }
818
819        if (parsedArgs.runtimeInit) {
820            if (parsedArgs.invokeWith != null) {
821                WrapperInit.execApplication(parsedArgs.invokeWith,
822                        parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs);
823            } else {
824                RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
825            }
826        } else {
827            String className;
828            try {
829                className = parsedArgs.remainingArgs[0];
830            } catch (ArrayIndexOutOfBoundsException ex) {
831                logAndPrintError(newStderr,
832                        "Missing required class name argument", null);
833                return;
834            }
835
836            String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
837            System.arraycopy(parsedArgs.remainingArgs, 1,
838                    mainArgs, 0, mainArgs.length);
839
840            if (parsedArgs.invokeWith != null) {
841                WrapperInit.execStandalone(parsedArgs.invokeWith,
842                        parsedArgs.classpath, className, mainArgs);
843            } else {
844                ClassLoader cloader;
845                if (parsedArgs.classpath != null) {
846                    cloader = new PathClassLoader(parsedArgs.classpath,
847                            ClassLoader.getSystemClassLoader());
848                } else {
849                    cloader = ClassLoader.getSystemClassLoader();
850                }
851
852                try {
853                    ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
854                } catch (RuntimeException ex) {
855                    logAndPrintError(newStderr, "Error starting.", ex);
856                }
857            }
858        }
859    }
860
861    /**
862     * Handles post-fork cleanup of parent proc
863     *
864     * @param pid != 0; pid of child if &gt; 0 or indication of failed fork
865     * if &lt; 0;
866     * @param descriptors null-ok; file descriptors for child's new stdio if
867     * specified.
868     * @param pipeFd null-ok; pipe for communication with child.
869     * @param parsedArgs non-null; zygote args
870     * @return true for "exit command loop" and false for "continue command
871     * loop"
872     */
873    private boolean handleParentProc(int pid,
874            FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
875
876        if (pid > 0) {
877            setChildPgid(pid);
878        }
879
880        if (descriptors != null) {
881            for (FileDescriptor fd: descriptors) {
882                IoUtils.closeQuietly(fd);
883            }
884        }
885
886        if (pipeFd != null && pid > 0) {
887            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
888            int innerPid = -1;
889            try {
890                innerPid = is.readInt();
891            } catch (IOException ex) {
892                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
893            } finally {
894                try {
895                    is.close();
896                } catch (IOException ex) {
897                }
898            }
899
900            // Ensure that the pid reported by the wrapped process is either the
901            // child process that we forked, or a descendant of it.
902            if (innerPid > 0) {
903                int parentPid = innerPid;
904                while (parentPid > 0 && parentPid != pid) {
905                    parentPid = Process.getParentPid(parentPid);
906                }
907                if (parentPid > 0) {
908                    Log.i(TAG, "Wrapped process has pid " + innerPid);
909                    pid = innerPid;
910                } else {
911                    Log.w(TAG, "Wrapped process reported a pid that is not a child of "
912                            + "the process that we forked: childPid=" + pid
913                            + " innerPid=" + innerPid);
914                }
915            }
916        }
917
918        try {
919            mSocketOutStream.writeInt(pid);
920        } catch (IOException ex) {
921            Log.e(TAG, "Error reading from command socket", ex);
922            return true;
923        }
924
925        /*
926         * If the peer wants to use the socket to wait on the
927         * newly spawned process, then we're all done.
928         */
929        if (parsedArgs.peerWait) {
930            try {
931                mSocket.close();
932            } catch (IOException ex) {
933                Log.e(TAG, "Zygote: error closing sockets", ex);
934            }
935            return true;
936        }
937        return false;
938    }
939
940    private void setChildPgid(int pid) {
941        // Try to move the new child into the peer's process group.
942        try {
943            ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
944        } catch (IOException ex) {
945            // This exception is expected in the case where
946            // the peer is not in our session
947            // TODO get rid of this log message in the case where
948            // getsid(0) != getsid(peer.getPid())
949            Log.i(TAG, "Zygote: setpgid failed. This is "
950                + "normal if peer is not in our session");
951        }
952    }
953
954    /**
955     * Logs an error message and prints it to the specified stream, if
956     * provided
957     *
958     * @param newStderr null-ok; a standard error stream
959     * @param message non-null; error message
960     * @param ex null-ok an exception
961     */
962    private static void logAndPrintError (PrintStream newStderr,
963            String message, Throwable ex) {
964        Log.e(TAG, message, ex);
965        if (newStderr != null) {
966            newStderr.println(message + (ex == null ? "" : ex));
967        }
968    }
969}
970