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