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