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