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