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      */
ZygoteConnection(LocalSocket socket, String abiList)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      */
checkTime(long startTime, String where)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      */
getFileDescriptor()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      */
runOnce()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 
handleAbiListQuery()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      */
closeSocket()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          */
Arguments(String args[])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          */
parseArgs(String args[])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      */
readArgumentList()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      */
applyUidSecurityPolicy(Arguments args, Credentials peer, String peerSecurityContext)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      */
applyDebuggerSystemProperty(Arguments args)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      */
applyRlimitSecurityPolicy( Arguments args, Credentials peer, String peerSecurityContext)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      */
applyInvokeWithSecurityPolicy(Arguments args, Credentials peer, String peerSecurityContext)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      */
applyseInfoSecurityPolicy( Arguments args, Credentials peer, String peerSecurityContext)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      */
applyInvokeWithSystemProperty(Arguments args)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      */
handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)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      */
handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs)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 
setChildPgid(int pid)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      */
logAndPrintError(PrintStream newStderr, String message, Throwable ex)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