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