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