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