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