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