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