blob: 85d84bb3f98679722b69815d41b981ff1860a8ff [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package com.android.internal.os;
18
Narayan Kamath6ac7e672015-01-16 16:26:54 +000019import static android.system.OsConstants.F_SETFD;
Elliott Hughes3fe59512014-12-12 14:07:34 -080020import static android.system.OsConstants.O_CLOEXEC;
Elliott Hughesdac83f52014-12-15 11:00:25 -080021import static android.system.OsConstants.STDERR_FILENO;
22import static android.system.OsConstants.STDIN_FILENO;
23import static android.system.OsConstants.STDOUT_FILENO;
Elliott Hughes3fe59512014-12-12 14:07:34 -080024
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.net.Credentials;
26import android.net.LocalSocket;
27import android.os.Process;
Jeff Sharkey5b1ada22012-08-14 18:47:09 -070028import android.os.SELinux;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.SystemProperties;
Narayan Kamathfbb32f62015-06-12 15:34:35 +010030import android.os.Trace;
Elliott Hughes860c5912014-04-28 19:19:13 -070031import android.system.ErrnoException;
32import android.system.Os;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.Log;
Narayan Kamath37ad4b02015-01-19 16:05:24 +000034import dalvik.system.VMRuntime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import java.io.BufferedReader;
Jeff Brownebed7d62011-05-16 17:08:42 -070036import java.io.DataInputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import java.io.DataOutputStream;
38import java.io.FileDescriptor;
Jeff Brownebed7d62011-05-16 17:08:42 -070039import java.io.FileInputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import java.io.FileOutputStream;
41import java.io.IOException;
42import java.io.InputStreamReader;
43import java.io.PrintStream;
Narayan Kamathc41638c2014-04-07 13:56:15 +010044import java.nio.charset.StandardCharsets;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import java.util.ArrayList;
Jeff Brownebed7d62011-05-16 17:08:42 -070046import libcore.io.IoUtils;
Jeff Brownebed7d62011-05-16 17:08:42 -070047
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048/**
49 * A connection that can make spawn requests.
50 */
51class 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 */
Narayan Kamathc41638c2014-04-07 13:56:15 +010067 private static final int MAX_ZYGOTE_ARGC = 1024;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068
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;
Narayan Kamathc41638c2014-04-07 13:56:15 +010080 private final String abiList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 * Constructs instance from connected socket.
84 *
85 * @param socket non-null; connected socket
Narayan Kamathc41638c2014-04-07 13:56:15 +010086 * @param abiList non-null; a list of ABIs this zygote supports.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 * @throws IOException
88 */
Narayan Kamathc41638c2014-04-07 13:56:15 +010089 ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 mSocket = socket;
Narayan Kamathc41638c2014-04-07 13:56:15 +010091 this.abiList = abiList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
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);
dcashmanfc4c0bf82015-03-05 17:17:47 -0800100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 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 */
114 FileDescriptor getFileDesciptor() {
115 return mSocket.getFileDescriptor();
116 }
117
118 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 * 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 */
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
Jeff Brownebed7d62011-05-16 17:08:42 -0700161 int pid = -1;
162 FileDescriptor childPipeFd = null;
163 FileDescriptor serverPipeFd = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
165 try {
166 parsedArgs = new Arguments(args);
Narayan Kamathc41638c2014-04-07 13:56:15 +0100167
168 if (parsedArgs.abiListQuery) {
169 return handleAbiListQuery();
170 }
171
Elliott Hughes42a4bb52013-11-07 17:21:03 -0800172 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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
dcashmanfc4c0bf82015-03-05 17:17:47 -0800178 applyUidSecurityPolicy(parsedArgs, peer);
179 applyInvokeWithSecurityPolicy(parsedArgs, peer);
Jeff Brownebed7d62011-05-16 17:08:42 -0700180
181 applyDebuggerSystemProperty(parsedArgs);
182 applyInvokeWithSystemProperty(parsedArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183
184 int[][] rlimits = null;
185
186 if (parsedArgs.rlimits != null) {
187 rlimits = parsedArgs.rlimits.toArray(intArray2d);
188 }
189
Narayan Kamathf48029f2015-01-08 12:45:37 +0000190 if (parsedArgs.invokeWith != null) {
Elliott Hughes3fe59512014-12-12 14:07:34 -0800191 FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
Jeff Brownebed7d62011-05-16 17:08:42 -0700192 childPipeFd = pipeFds[1];
193 serverPipeFd = pipeFds[0];
Narayan Kamath6ac7e672015-01-16 16:26:54 +0000194 Os.fcntlInt(childPipeFd, F_SETFD, 0);
Jeff Brownebed7d62011-05-16 17:08:42 -0700195 }
196
Dave Platt89d4c892014-02-05 17:06:42 -0800197 /**
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
Jeff Sharkey5b1ada22012-08-14 18:47:09 -0700225 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
226 parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
jgu212eacd062014-09-10 06:55:07 -0400227 parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
228 parsedArgs.appDataDir);
Jeff Brownebed7d62011-05-16 17:08:42 -0700229 } catch (ErrnoException ex) {
230 logAndPrintError(newStderr, "Exception creating pipe", ex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 } catch (IllegalArgumentException ex) {
Jeff Brownebed7d62011-05-16 17:08:42 -0700232 logAndPrintError(newStderr, "Invalid zygote arguments", ex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 } catch (ZygoteSecurityException ex) {
234 logAndPrintError(newStderr,
235 "Zygote security policy prevents request: ", ex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 }
237
Jeff Brownebed7d62011-05-16 17:08:42 -0700238 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 }
258 }
259
Narayan Kamathc41638c2014-04-07 13:56:15 +0100260 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 /**
273 * Closes socket associated with this connection.
274 */
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 /**
Jeff Brownebed7d62011-05-16 17:08:42 -0700285 * Handles argument parsing for args related to the zygote spawner.
286 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 * 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>
Andreas Gampeaec67dc2014-09-02 21:23:06 -0700302 * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
Narayan Kamathf48029f2015-01-08 12:45:37 +0000303 * <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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 * </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
Ben Cheng23085b72010-02-08 16:06:32 -0800323 /**
Elliott Hughesae07ecf2011-07-06 17:33:27 -0700324 * From --enable-debugger, --enable-checkjni, --enable-assert,
Nicolas Geoffray9abbf452015-11-05 11:29:42 +0000325 * --enable-safemode, --generate-debug-info and --enable-jni-logging.
Ben Cheng23085b72010-02-08 16:06:32 -0800326 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 int debugFlags;
328
Jeff Sharkey5b1ada22012-08-14 18:47:09 -0700329 /** From --mount-external */
330 int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
331
Elliott Hughese1dfcb72011-07-08 11:08:07 -0700332 /** from --target-sdk-version. */
333 int targetSdkVersion;
334 boolean targetSdkVersionSpecified;
335
Jeff Brownebed7d62011-05-16 17:08:42 -0700336 /** from --nice-name */
337 String niceName;
338
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 /** from --capabilities */
340 boolean capabilitiesSpecified;
341 long permittedCapabilities;
342 long effectiveCapabilities;
343
Stephen Smalley83d9eda2012-01-13 08:34:17 -0500344 /** from --seinfo */
345 boolean seInfoSpecified;
346 String seInfo;
347
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 /** from all --rlimit=r,c,m */
349 ArrayList<int[]> rlimits;
350
Jeff Brownebed7d62011-05-16 17:08:42 -0700351 /** from --invoke-with */
352 String invokeWith;
353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 /**
355 * Any args after and including the first non-option arg
356 * (or after a '--')
357 */
358 String remainingArgs[];
359
360 /**
Narayan Kamathc41638c2014-04-07 13:56:15 +0100361 * Whether the current arguments constitute an ABI list query.
362 */
363 boolean abiListQuery;
364
365 /**
Andreas Gampeaec67dc2014-09-02 21:23:06 -0700366 * The instruction set to use, or null when not important.
367 */
368 String instructionSet;
369
370 /**
jgu212eacd062014-09-10 06:55:07 -0400371 * 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 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 * Constructs instance and parses args
378 * @param args zygote command-line args
379 * @throws IllegalArgumentException
380 */
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 */
393 private void parseArgs(String args[])
394 throws IllegalArgumentException {
395 int curArg = 0;
396
Narayan Kamathb6b044a2015-02-13 17:31:25 +0000397 boolean seenRuntimeArgs = false;
Narayan Kamathf48029f2015-01-08 12:45:37 +0000398
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 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));
Elliott Hughese1dfcb72011-07-08 11:08:07 -0700421 } 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));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 } else if (arg.equals("--enable-debugger")) {
430 debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
Ben Cheng23085b72010-02-08 16:06:32 -0800431 } else if (arg.equals("--enable-safemode")) {
432 debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 } else if (arg.equals("--enable-checkjni")) {
434 debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
David Srbecky065075e2015-05-28 17:16:09 +0100435 } else if (arg.equals("--generate-debug-info")) {
436 debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
Tamas Berghammerdf6cb282016-01-29 12:07:00 +0000437 } 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;
Elliott Hughesae07ecf2011-07-06 17:33:27 -0700441 } else if (arg.equals("--enable-jni-logging")) {
442 debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 } else if (arg.equals("--enable-assert")) {
444 debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
Narayan Kamathf48029f2015-01-08 12:45:37 +0000445 } else if (arg.equals("--runtime-args")) {
446 seenRuntimeArgs = true;
Stephen Smalley83d9eda2012-01-13 08:34:17 -0500447 } 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 } 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 } 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 }
Jeff Brownebed7d62011-05-16 17:08:42 -0700505 } 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);
Jeff Sharkey48877892015-03-18 11:27:19 -0700522 } else if (arg.equals("--mount-external-default")) {
523 mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
Jeff Sharkey9527b222015-06-24 15:24:48 -0700524 } 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;
Narayan Kamathc41638c2014-04-07 13:56:15 +0100528 } else if (arg.equals("--query-abi-list")) {
529 abiListQuery = true;
Andreas Gampeaec67dc2014-09-02 21:23:06 -0700530 } else if (arg.startsWith("--instruction-set=")) {
531 instructionSet = arg.substring(arg.indexOf('=') + 1);
jgu212eacd062014-09-10 06:55:07 -0400532 } else if (arg.startsWith("--app-data-dir=")) {
533 appDataDir = arg.substring(arg.indexOf('=') + 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 } else {
535 break;
536 }
537 }
538
Narayan Kamathb6b044a2015-02-13 17:31:25 +0000539 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 }
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 */
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
dcashmanfc4c0bf82015-03-05 17:17:47 -0800588 if (argc > MAX_ZYGOTE_ARGC) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 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 /**
dcashmanfc4c0bf82015-03-05 17:17:47 -0800605 * uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 * operation. It may also specify any gid and setgroups() list it chooses.
607 * In factory test mode, it may specify any UID.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 *
609 * @param args non-null; zygote spawner arguments
610 * @param peer non-null; peer credentials
611 * @throws ZygoteSecurityException
612 */
dcashmanfc4c0bf82015-03-05 17:17:47 -0800613 private static void applyUidSecurityPolicy(Arguments args, Credentials peer)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 throws ZygoteSecurityException {
615
dcashmanfc4c0bf82015-03-05 17:17:47 -0800616 if (peer.getUid() == Process.SYSTEM_UID) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 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 */
dcashmanfc4c0bf82015-03-05 17:17:47 -0800623 uidRestricted = !(factoryTest.equals("1") || factoryTest.equals("2"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624
dcashmanfc4c0bf82015-03-05 17:17:47 -0800625 if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 throw new ZygoteSecurityException(
627 "System UID may not launch process with UID < "
dcashmanfc4c0bf82015-03-05 17:17:47 -0800628 + Process.SYSTEM_UID);
Stephen Smalley83d9eda2012-01-13 08:34:17 -0500629 }
630 }
631
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 /**
Jeff Brownebed7d62011-05-16 17:08:42 -0700644 * Applies debugger system properties to the zygote arguments.
645 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 * 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 */
Jeff Brownebed7d62011-05-16 17:08:42 -0700652 public static void applyDebuggerSystemProperty(Arguments args) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 if ("1".equals(SystemProperties.get("ro.debuggable"))) {
654 args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
655 }
656 }
657
658 /**
Jeff Brownebed7d62011-05-16 17:08:42 -0700659 * 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 */
dcashmanfc4c0bf82015-03-05 17:17:47 -0800671 private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
Jeff Brownebed7d62011-05-16 17:08:42 -0700672 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 *
Narayan Kamath973b4662014-03-31 13:41:26 +0100684 * @param args non-null; zygote args
Jeff Brownebed7d62011-05-16 17:08:42 -0700685 */
686 public static void applyInvokeWithSystemProperty(Arguments args) {
687 if (args.invokeWith == null && args.niceName != null) {
dcashmanfc4c0bf82015-03-05 17:17:47 -0800688 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);
Jeff Brownebed7d62011-05-16 17:08:42 -0700695 }
dcashmanfc4c0bf82015-03-05 17:17:47 -0800696 }
697 args.invokeWith = SystemProperties.get(property);
698 if (args.invokeWith != null && args.invokeWith.length() == 0) {
699 args.invokeWith = null;
Jeff Brownebed7d62011-05-16 17:08:42 -0700700 }
701 }
702 }
703
704 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 * 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.
Jeff Brownebed7d62011-05-16 17:08:42 -0700711 * @param pipeFd null-ok; pipe for communication back to Zygote.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 * @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 */
718 private void handleChildProc(Arguments parsedArgs,
Jeff Brownebed7d62011-05-16 17:08:42 -0700719 FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 throws ZygoteInit.MethodAndArgsCaller {
Dave Platt89d4c892014-02-05 17:06:42 -0800721 /**
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
Nick Kralevich468f6c12013-01-30 08:45:03 -0800727 closeSocket();
728 ZygoteInit.closeServerSocket();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729
730 if (descriptors != null) {
731 try {
Elliott Hughesdac83f52014-12-15 11:00:25 -0800732 Os.dup2(descriptors[0], STDIN_FILENO);
733 Os.dup2(descriptors[1], STDOUT_FILENO);
734 Os.dup2(descriptors[2], STDERR_FILENO);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735
736 for (FileDescriptor fd: descriptors) {
Jeff Brownebed7d62011-05-16 17:08:42 -0700737 IoUtils.closeQuietly(fd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 }
739 newStderr = System.err;
Elliott Hughesdac83f52014-12-15 11:00:25 -0800740 } catch (ErrnoException ex) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 Log.e(TAG, "Error reopening stdio", ex);
742 }
743 }
744
Jeff Brownebed7d62011-05-16 17:08:42 -0700745 if (parsedArgs.niceName != null) {
746 Process.setArgV0(parsedArgs.niceName);
747 }
748
Narayan Kamathfbb32f62015-06-12 15:34:35 +0100749 // End of the postFork event.
750 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Narayan Kamathf48029f2015-01-08 12:45:37 +0000751 if (parsedArgs.invokeWith != null) {
752 WrapperInit.execApplication(parsedArgs.invokeWith,
753 parsedArgs.niceName, parsedArgs.targetSdkVersion,
Narayan Kamath37ad4b02015-01-19 16:05:24 +0000754 VMRuntime.getCurrentInstructionSet(),
Narayan Kamathf48029f2015-01-08 12:45:37 +0000755 pipeFd, parsedArgs.remainingArgs);
Jeff Brownebed7d62011-05-16 17:08:42 -0700756 } else {
Narayan Kamathf48029f2015-01-08 12:45:37 +0000757 RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
758 parsedArgs.remainingArgs, null /* classLoader */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
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.
Jeff Brownebed7d62011-05-16 17:08:42 -0700769 * @param pipeFd null-ok; pipe for communication with child.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 * @param parsedArgs non-null; zygote args
771 * @return true for "exit command loop" and false for "continue command
772 * loop"
773 */
774 private boolean handleParentProc(int pid,
Jeff Brownebed7d62011-05-16 17:08:42 -0700775 FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776
Jeff Brownebed7d62011-05-16 17:08:42 -0700777 if (pid > 0) {
778 setChildPgid(pid);
779 }
780
781 if (descriptors != null) {
782 for (FileDescriptor fd: descriptors) {
783 IoUtils.closeQuietly(fd);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 }
785 }
786
Jeff Brown3f9dd282011-07-08 20:02:19 -0700787 boolean usingWrapper = false;
Jeff Brownebed7d62011-05-16 17:08:42 -0700788 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) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 }
800 }
Jeff Brownebed7d62011-05-16 17:08:42 -0700801
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;
Jeff Brown3f9dd282011-07-08 20:02:19 -0700812 usingWrapper = true;
Jeff Brownebed7d62011-05-16 17:08:42 -0700813 } 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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 }
820
821 try {
822 mSocketOutStream.writeInt(pid);
Jeff Brown3f9dd282011-07-08 20:02:19 -0700823 mSocketOutStream.writeBoolean(usingWrapper);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 } catch (IOException ex) {
Narayan Kamathc41638c2014-04-07 13:56:15 +0100825 Log.e(TAG, "Error writing to command socket", ex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 return true;
827 }
828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 return false;
830 }
831
Jeff Brownebed7d62011-05-16 17:08:42 -0700832 private void setChildPgid(int pid) {
833 // Try to move the new child into the peer's process group.
834 try {
Elliott Hughes26b56e62014-12-17 12:28:29 -0800835 Os.setpgid(pid, Os.getpgid(peer.getPid()));
836 } catch (ErrnoException ex) {
Jeff Brownebed7d62011-05-16 17:08:42 -0700837 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 /**
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 */
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}