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