ZygoteConnection.java revision ebed7d6e35f7f960e6e6add2b8ab7c7a31a511c3
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 17package com.android.internal.os; 18 19import android.net.Credentials; 20import android.net.LocalSocket; 21import android.os.Process; 22import android.os.SystemProperties; 23import android.util.Log; 24 25import dalvik.system.PathClassLoader; 26import dalvik.system.Zygote; 27 28import java.io.BufferedReader; 29import java.io.DataInputStream; 30import java.io.DataOutputStream; 31import java.io.FileDescriptor; 32import java.io.FileInputStream; 33import java.io.FileOutputStream; 34import java.io.IOException; 35import java.io.InputStreamReader; 36import java.io.PrintStream; 37import java.util.ArrayList; 38 39import libcore.io.ErrnoException; 40import libcore.io.IoUtils; 41import libcore.io.Libcore; 42 43/** 44 * A connection that can make spawn requests. 45 */ 46class ZygoteConnection { 47 private static final String TAG = "Zygote"; 48 49 /** a prototype instance for a future List.toArray() */ 50 private static final int[][] intArray2d = new int[0][0]; 51 52 /** 53 * {@link android.net.LocalSocket#setSoTimeout} value for connections. 54 * Effectively, the amount of time a requestor has between the start of 55 * the request and the completed request. The select-loop mode Zygote 56 * doesn't have the logic to return to the select loop in the middle of 57 * a request, so we need to time out here to avoid being denial-of-serviced. 58 */ 59 private static final int CONNECTION_TIMEOUT_MILLIS = 1000; 60 61 /** max number of arguments that a connection can specify */ 62 private static final int MAX_ZYGOTE_ARGC=1024; 63 64 /** 65 * The command socket. 66 * 67 * mSocket is retained in the child process in "peer wait" mode, so 68 * that it closes when the child process terminates. In other cases, 69 * it is closed in the peer. 70 */ 71 private final LocalSocket mSocket; 72 private final DataOutputStream mSocketOutStream; 73 private final BufferedReader mSocketReader; 74 private final Credentials peer; 75 76 /** 77 * A long-lived reference to the original command socket used to launch 78 * this peer. If "peer wait" mode is specified, the process that requested 79 * the new VM instance intends to track the lifetime of the spawned instance 80 * via the command socket. In this case, the command socket is closed 81 * in the Zygote and placed here in the spawned instance so that it will 82 * not be collected and finalized. This field remains null at all times 83 * in the original Zygote process, and in all spawned processes where 84 * "peer-wait" mode was not requested. 85 */ 86 private static LocalSocket sPeerWaitSocket = null; 87 88 /** 89 * Constructs instance from connected socket. 90 * 91 * @param socket non-null; connected socket 92 * @throws IOException 93 */ 94 ZygoteConnection(LocalSocket socket) throws IOException { 95 mSocket = socket; 96 97 mSocketOutStream 98 = new DataOutputStream(socket.getOutputStream()); 99 100 mSocketReader = new BufferedReader( 101 new InputStreamReader(socket.getInputStream()), 256); 102 103 mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS); 104 105 try { 106 peer = mSocket.getPeerCredentials(); 107 } catch (IOException ex) { 108 Log.e(TAG, "Cannot read peer credentials", ex); 109 throw ex; 110 } 111 } 112 113 /** 114 * Returns the file descriptor of the associated socket. 115 * 116 * @return null-ok; file descriptor 117 */ 118 FileDescriptor getFileDesciptor() { 119 return mSocket.getFileDescriptor(); 120 } 121 122 /** 123 * Reads start commands from an open command socket. 124 * Start commands are presently a pair of newline-delimited lines 125 * indicating a) class to invoke main() on b) nice name to set argv[0] to. 126 * Continues to read commands and forkAndSpecialize children until 127 * the socket is closed. This method is used in ZYGOTE_FORK_MODE 128 * 129 * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() 130 * method in child process 131 */ 132 void run() throws ZygoteInit.MethodAndArgsCaller { 133 134 int loopCount = ZygoteInit.GC_LOOP_COUNT; 135 136 while (true) { 137 /* 138 * Call gc() before we block in readArgumentList(). 139 * It's work that has to be done anyway, and it's better 140 * to avoid making every child do it. It will also 141 * madvise() any free memory as a side-effect. 142 * 143 * Don't call it every time, because walking the entire 144 * heap is a lot of overhead to free a few hundred bytes. 145 */ 146 if (loopCount <= 0) { 147 ZygoteInit.gc(); 148 loopCount = ZygoteInit.GC_LOOP_COUNT; 149 } else { 150 loopCount--; 151 } 152 153 if (runOnce()) { 154 break; 155 } 156 } 157 } 158 159 /** 160 * Reads one start command from the command socket. If successful, 161 * a child is forked and a {@link ZygoteInit.MethodAndArgsCaller} 162 * exception is thrown in that child while in the parent process, 163 * the method returns normally. On failure, the child is not 164 * spawned and messages are printed to the log and stderr. Returns 165 * a boolean status value indicating whether an end-of-file on the command 166 * socket has been encountered. 167 * 168 * @return false if command socket should continue to be read from, or 169 * true if an end-of-file has been encountered. 170 * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main() 171 * method in child process 172 */ 173 boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { 174 175 String args[]; 176 Arguments parsedArgs = null; 177 FileDescriptor[] descriptors; 178 179 try { 180 args = readArgumentList(); 181 descriptors = mSocket.getAncillaryFileDescriptors(); 182 } catch (IOException ex) { 183 Log.w(TAG, "IOException on command socket " + ex.getMessage()); 184 closeSocket(); 185 return true; 186 } 187 188 if (args == null) { 189 // EOF reached. 190 closeSocket(); 191 return true; 192 } 193 194 /** the stderr of the most recent request, if avail */ 195 PrintStream newStderr = null; 196 197 if (descriptors != null && descriptors.length >= 3) { 198 newStderr = new PrintStream( 199 new FileOutputStream(descriptors[2])); 200 } 201 202 int pid = -1; 203 FileDescriptor childPipeFd = null; 204 FileDescriptor serverPipeFd = null; 205 206 try { 207 parsedArgs = new Arguments(args); 208 209 applyUidSecurityPolicy(parsedArgs, peer); 210 applyRlimitSecurityPolicy(parsedArgs, peer); 211 applyCapabilitiesSecurityPolicy(parsedArgs, peer); 212 applyInvokeWithSecurityPolicy(parsedArgs, peer); 213 214 applyDebuggerSystemProperty(parsedArgs); 215 applyInvokeWithSystemProperty(parsedArgs); 216 217 int[][] rlimits = null; 218 219 if (parsedArgs.rlimits != null) { 220 rlimits = parsedArgs.rlimits.toArray(intArray2d); 221 } 222 223 if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { 224 FileDescriptor[] pipeFds = Libcore.os.pipe(); 225 childPipeFd = pipeFds[1]; 226 serverPipeFd = pipeFds[0]; 227 ZygoteInit.setCloseOnExec(serverPipeFd, true); 228 } 229 230 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, 231 parsedArgs.gids, parsedArgs.debugFlags, rlimits); 232 } catch (IOException ex) { 233 logAndPrintError(newStderr, "Exception creating pipe", ex); 234 } catch (ErrnoException ex) { 235 logAndPrintError(newStderr, "Exception creating pipe", ex); 236 } catch (IllegalArgumentException ex) { 237 logAndPrintError(newStderr, "Invalid zygote arguments", ex); 238 } catch (ZygoteSecurityException ex) { 239 logAndPrintError(newStderr, 240 "Zygote security policy prevents request: ", ex); 241 } 242 243 try { 244 if (pid == 0) { 245 // in child 246 IoUtils.closeQuietly(serverPipeFd); 247 serverPipeFd = null; 248 handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); 249 250 // should never get here, the child is expected to either 251 // throw ZygoteInit.MethodAndArgsCaller or exec(). 252 return true; 253 } else { 254 // in parent...pid of < 0 means failure 255 IoUtils.closeQuietly(childPipeFd); 256 childPipeFd = null; 257 return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); 258 } 259 } finally { 260 IoUtils.closeQuietly(childPipeFd); 261 IoUtils.closeQuietly(serverPipeFd); 262 } 263 } 264 265 /** 266 * Closes socket associated with this connection. 267 */ 268 void closeSocket() { 269 try { 270 mSocket.close(); 271 } catch (IOException ex) { 272 Log.e(TAG, "Exception while closing command " 273 + "socket in parent", ex); 274 } 275 } 276 277 /** 278 * Handles argument parsing for args related to the zygote spawner. 279 * 280 * Current recognized args: 281 * <ul> 282 * <li> --setuid=<i>uid of child process, defaults to 0</i> 283 * <li> --setgid=<i>gid of child process, defaults to 0</i> 284 * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i> 285 * <li> --capabilities=<i>a pair of comma-separated integer strings 286 * indicating Linux capabilities(2) set for child. The first string 287 * represents the <code>permitted</code> set, and the second the 288 * <code>effective</code> set. Precede each with 0 or 289 * 0x for octal or hexidecimal value. If unspecified, both default to 0. 290 * This parameter is only applied if the uid of the new process will 291 * be non-0. </i> 292 * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call. 293 * <code>r</code> is the resource, <code>c</code> and <code>m</code> 294 * are the settings for current and max value.</i> 295 * <li> --peer-wait indicates that the command socket should 296 * be inherited by (and set to close-on-exec in) the spawned process 297 * and used to track the lifetime of that process. The spawning process 298 * then exits. Without this flag, it is retained by the spawning process 299 * (and closed in the child) in expectation of a new spawn request. 300 * <li> --classpath=<i>colon-separated classpath</i> indicates 301 * that the specified class (which must b first non-flag argument) should 302 * be loaded from jar files in the specified classpath. Incompatible with 303 * --runtime-init 304 * <li> --runtime-init 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> --nice-name=<i>nice name to appear in ps</i> 309 * <li> If <code>--runtime-init</code> is present: 310 * [--] <args for RuntimeInit > 311 * <li> If <code>--runtime-init</code> is absent: 312 * [--] <classname> [args...] 313 * </ul> 314 */ 315 static class Arguments { 316 /** from --setuid */ 317 int uid = 0; 318 boolean uidSpecified; 319 320 /** from --setgid */ 321 int gid = 0; 322 boolean gidSpecified; 323 324 /** from --setgroups */ 325 int[] gids; 326 327 /** from --peer-wait */ 328 boolean peerWait; 329 330 /** 331 * From --enable-debugger, --enable-checkjni, --enable-assert, and 332 * --enable-safemode 333 */ 334 int debugFlags; 335 336 /** from --classpath */ 337 String classpath; 338 339 /** from --runtime-init */ 340 boolean runtimeInit; 341 342 /** from --nice-name */ 343 String niceName; 344 345 /** from --capabilities */ 346 boolean capabilitiesSpecified; 347 long permittedCapabilities; 348 long effectiveCapabilities; 349 350 /** from all --rlimit=r,c,m */ 351 ArrayList<int[]> rlimits; 352 353 /** from --invoke-with */ 354 String invokeWith; 355 356 /** 357 * Any args after and including the first non-option arg 358 * (or after a '--') 359 */ 360 String remainingArgs[]; 361 362 /** 363 * Constructs instance and parses args 364 * @param args zygote command-line args 365 * @throws IllegalArgumentException 366 */ 367 Arguments(String args[]) throws IllegalArgumentException { 368 parseArgs(args); 369 } 370 371 /** 372 * Parses the commandline arguments intended for the Zygote spawner 373 * (such as "--setuid=" and "--setgid=") and creates an array 374 * containing the remaining args. 375 * 376 * Per security review bug #1112214, duplicate args are disallowed in 377 * critical cases to make injection harder. 378 */ 379 private void parseArgs(String args[]) 380 throws IllegalArgumentException { 381 int curArg = 0; 382 383 for ( /* curArg */ ; curArg < args.length; curArg++) { 384 String arg = args[curArg]; 385 386 if (arg.equals("--")) { 387 curArg++; 388 break; 389 } else if (arg.startsWith("--setuid=")) { 390 if (uidSpecified) { 391 throw new IllegalArgumentException( 392 "Duplicate arg specified"); 393 } 394 uidSpecified = true; 395 uid = Integer.parseInt( 396 arg.substring(arg.indexOf('=') + 1)); 397 } else if (arg.startsWith("--setgid=")) { 398 if (gidSpecified) { 399 throw new IllegalArgumentException( 400 "Duplicate arg specified"); 401 } 402 gidSpecified = true; 403 gid = Integer.parseInt( 404 arg.substring(arg.indexOf('=') + 1)); 405 } else if (arg.equals("--enable-debugger")) { 406 debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; 407 } else if (arg.equals("--enable-safemode")) { 408 debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; 409 } else if (arg.equals("--enable-checkjni")) { 410 debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; 411 } else if (arg.equals("--enable-assert")) { 412 debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; 413 } else if (arg.equals("--peer-wait")) { 414 peerWait = true; 415 } else if (arg.equals("--runtime-init")) { 416 runtimeInit = true; 417 } else if (arg.startsWith("--capabilities=")) { 418 if (capabilitiesSpecified) { 419 throw new IllegalArgumentException( 420 "Duplicate arg specified"); 421 } 422 capabilitiesSpecified = true; 423 String capString = arg.substring(arg.indexOf('=')+1); 424 425 String[] capStrings = capString.split(",", 2); 426 427 if (capStrings.length == 1) { 428 effectiveCapabilities = Long.decode(capStrings[0]); 429 permittedCapabilities = effectiveCapabilities; 430 } else { 431 permittedCapabilities = Long.decode(capStrings[0]); 432 effectiveCapabilities = Long.decode(capStrings[1]); 433 } 434 } else if (arg.startsWith("--rlimit=")) { 435 // Duplicate --rlimit arguments are specifically allowed. 436 String[] limitStrings 437 = arg.substring(arg.indexOf('=')+1).split(","); 438 439 if (limitStrings.length != 3) { 440 throw new IllegalArgumentException( 441 "--rlimit= should have 3 comma-delimited ints"); 442 } 443 int[] rlimitTuple = new int[limitStrings.length]; 444 445 for(int i=0; i < limitStrings.length; i++) { 446 rlimitTuple[i] = Integer.parseInt(limitStrings[i]); 447 } 448 449 if (rlimits == null) { 450 rlimits = new ArrayList(); 451 } 452 453 rlimits.add(rlimitTuple); 454 } else if (arg.equals("-classpath")) { 455 if (classpath != null) { 456 throw new IllegalArgumentException( 457 "Duplicate arg specified"); 458 } 459 try { 460 classpath = args[++curArg]; 461 } catch (IndexOutOfBoundsException ex) { 462 throw new IllegalArgumentException( 463 "-classpath requires argument"); 464 } 465 } else if (arg.startsWith("--setgroups=")) { 466 if (gids != null) { 467 throw new IllegalArgumentException( 468 "Duplicate arg specified"); 469 } 470 471 String[] params 472 = arg.substring(arg.indexOf('=') + 1).split(","); 473 474 gids = new int[params.length]; 475 476 for (int i = params.length - 1; i >= 0 ; i--) { 477 gids[i] = Integer.parseInt(params[i]); 478 } 479 } else if (arg.equals("--invoke-with")) { 480 if (invokeWith != null) { 481 throw new IllegalArgumentException( 482 "Duplicate arg specified"); 483 } 484 try { 485 invokeWith = args[++curArg]; 486 } catch (IndexOutOfBoundsException ex) { 487 throw new IllegalArgumentException( 488 "--invoke-with requires argument"); 489 } 490 } else if (arg.startsWith("--nice-name=")) { 491 if (niceName != null) { 492 throw new IllegalArgumentException( 493 "Duplicate arg specified"); 494 } 495 niceName = arg.substring(arg.indexOf('=') + 1); 496 } else { 497 break; 498 } 499 } 500 501 if (runtimeInit && classpath != null) { 502 throw new IllegalArgumentException( 503 "--runtime-init and -classpath are incompatible"); 504 } 505 506 remainingArgs = new String[args.length - curArg]; 507 508 System.arraycopy(args, curArg, remainingArgs, 0, 509 remainingArgs.length); 510 } 511 } 512 513 /** 514 * Reads an argument list from the command socket/ 515 * @return Argument list or null if EOF is reached 516 * @throws IOException passed straight through 517 */ 518 private String[] readArgumentList() 519 throws IOException { 520 521 /** 522 * See android.os.Process.zygoteSendArgsAndGetPid() 523 * Presently the wire format to the zygote process is: 524 * a) a count of arguments (argc, in essence) 525 * b) a number of newline-separated argument strings equal to count 526 * 527 * After the zygote process reads these it will write the pid of 528 * the child or -1 on failure. 529 */ 530 531 int argc; 532 533 try { 534 String s = mSocketReader.readLine(); 535 536 if (s == null) { 537 // EOF reached. 538 return null; 539 } 540 argc = Integer.parseInt(s); 541 } catch (NumberFormatException ex) { 542 Log.e(TAG, "invalid Zygote wire format: non-int at argc"); 543 throw new IOException("invalid wire format"); 544 } 545 546 // See bug 1092107: large argc can be used for a DOS attack 547 if (argc > MAX_ZYGOTE_ARGC) { 548 throw new IOException("max arg count exceeded"); 549 } 550 551 String[] result = new String[argc]; 552 for (int i = 0; i < argc; i++) { 553 result[i] = mSocketReader.readLine(); 554 if (result[i] == null) { 555 // We got an unexpected EOF. 556 throw new IOException("truncated request"); 557 } 558 } 559 560 return result; 561 } 562 563 /** 564 * Applies zygote security policy per bugs #875058 and #1082165. 565 * Based on the credentials of the process issuing a zygote command: 566 * <ol> 567 * <li> uid 0 (root) may specify any uid, gid, and setgroups() list 568 * <li> uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal 569 * operation. It may also specify any gid and setgroups() list it chooses. 570 * In factory test mode, it may specify any UID. 571 * <li> Any other uid may not specify any uid, gid, or setgroups list. The 572 * uid and gid will be inherited from the requesting process. 573 * </ul> 574 * 575 * @param args non-null; zygote spawner arguments 576 * @param peer non-null; peer credentials 577 * @throws ZygoteSecurityException 578 */ 579 private static void applyUidSecurityPolicy(Arguments args, Credentials peer) 580 throws ZygoteSecurityException { 581 582 int peerUid = peer.getUid(); 583 584 if (peerUid == 0) { 585 // Root can do what it wants 586 } else if (peerUid == Process.SYSTEM_UID ) { 587 // System UID is restricted, except in factory test mode 588 String factoryTest = SystemProperties.get("ro.factorytest"); 589 boolean uidRestricted; 590 591 /* In normal operation, SYSTEM_UID can only specify a restricted 592 * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid. 593 */ 594 uidRestricted 595 = !(factoryTest.equals("1") || factoryTest.equals("2")); 596 597 if (uidRestricted 598 && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) { 599 throw new ZygoteSecurityException( 600 "System UID may not launch process with UID < " 601 + Process.SYSTEM_UID); 602 } 603 } else { 604 // Everything else 605 if (args.uidSpecified || args.gidSpecified 606 || args.gids != null) { 607 throw new ZygoteSecurityException( 608 "App UIDs may not specify uid's or gid's"); 609 } 610 } 611 612 // If not otherwise specified, uid and gid are inherited from peer 613 if (!args.uidSpecified) { 614 args.uid = peer.getUid(); 615 args.uidSpecified = true; 616 } 617 if (!args.gidSpecified) { 618 args.gid = peer.getGid(); 619 args.gidSpecified = true; 620 } 621 } 622 623 624 /** 625 * Applies debugger system properties to the zygote arguments. 626 * 627 * If "ro.debuggable" is "1", all apps are debuggable. Otherwise, 628 * the debugger state is specified via the "--enable-debugger" flag 629 * in the spawn request. 630 * 631 * @param args non-null; zygote spawner args 632 */ 633 public static void applyDebuggerSystemProperty(Arguments args) { 634 if ("1".equals(SystemProperties.get("ro.debuggable"))) { 635 args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; 636 } 637 } 638 639 /** 640 * Applies zygote security policy per bug #1042973. Based on the credentials 641 * of the process issuing a zygote command: 642 * <ol> 643 * <li> peers of uid 0 (root) and uid 1000 (Process.SYSTEM_UID) 644 * may specify any rlimits. 645 * <li> All other uids may not specify rlimits. 646 * </ul> 647 * @param args non-null; zygote spawner arguments 648 * @param peer non-null; peer credentials 649 * @throws ZygoteSecurityException 650 */ 651 private static void applyRlimitSecurityPolicy( 652 Arguments args, Credentials peer) 653 throws ZygoteSecurityException { 654 655 int peerUid = peer.getUid(); 656 657 if (!(peerUid == 0 || peerUid == Process.SYSTEM_UID)) { 658 // All peers with UID other than root or SYSTEM_UID 659 if (args.rlimits != null) { 660 throw new ZygoteSecurityException( 661 "This UID may not specify rlimits."); 662 } 663 } 664 } 665 666 /** 667 * Applies zygote security policy per bug #1042973. A root peer may 668 * spawn an instance with any capabilities. All other uids may spawn 669 * instances with any of the capabilities in the peer's permitted set 670 * but no more. 671 * 672 * @param args non-null; zygote spawner arguments 673 * @param peer non-null; peer credentials 674 * @throws ZygoteSecurityException 675 */ 676 private static void applyCapabilitiesSecurityPolicy( 677 Arguments args, Credentials peer) 678 throws ZygoteSecurityException { 679 680 if (args.permittedCapabilities == 0 681 && args.effectiveCapabilities == 0) { 682 // nothing to check 683 return; 684 } 685 686 if (peer.getUid() == 0) { 687 // root may specify anything 688 return; 689 } 690 691 long permittedCaps; 692 693 try { 694 permittedCaps = ZygoteInit.capgetPermitted(peer.getPid()); 695 } catch (IOException ex) { 696 throw new ZygoteSecurityException( 697 "Error retrieving peer's capabilities."); 698 } 699 700 /* 701 * Ensure that the client did not specify an effective set larger 702 * than the permitted set. The kernel will enforce this too, but we 703 * do it here to make the following check easier. 704 */ 705 if (((~args.permittedCapabilities) & args.effectiveCapabilities) != 0) { 706 throw new ZygoteSecurityException( 707 "Effective capabilities cannot be superset of " 708 + " permitted capabilities" ); 709 } 710 711 /* 712 * Ensure that the new permitted (and thus the new effective) set is 713 * a subset of the peer process's permitted set 714 */ 715 716 if (((~permittedCaps) & args.permittedCapabilities) != 0) { 717 throw new ZygoteSecurityException( 718 "Peer specified unpermitted capabilities" ); 719 } 720 } 721 722 /** 723 * Applies zygote security policy. 724 * Based on the credentials of the process issuing a zygote command: 725 * <ol> 726 * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a 727 * wrapper command. 728 * <li> Any other uid may not specify any invoke-with argument. 729 * </ul> 730 * 731 * @param args non-null; zygote spawner arguments 732 * @param peer non-null; peer credentials 733 * @throws ZygoteSecurityException 734 */ 735 private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer) 736 throws ZygoteSecurityException { 737 int peerUid = peer.getUid(); 738 739 if (args.invokeWith != null && peerUid != 0) { 740 throw new ZygoteSecurityException("Peer is not permitted to specify " 741 + "an explicit invoke-with wrapper command"); 742 } 743 } 744 745 /** 746 * Applies invoke-with system properties to the zygote arguments. 747 * 748 * @param parsedArgs non-null; zygote args 749 */ 750 public static void applyInvokeWithSystemProperty(Arguments args) { 751 if (args.invokeWith == null && args.niceName != null) { 752 if (args.niceName != null) { 753 String property = "wrap." + args.niceName; 754 if (property.length() > 31) { 755 property = property.substring(0, 31); 756 } 757 args.invokeWith = SystemProperties.get(property); 758 if (args.invokeWith != null && args.invokeWith.length() == 0) { 759 args.invokeWith = null; 760 } 761 } 762 } 763 } 764 765 /** 766 * Handles post-fork setup of child proc, closing sockets as appropriate, 767 * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller 768 * if successful or returning if failed. 769 * 770 * @param parsedArgs non-null; zygote args 771 * @param descriptors null-ok; new file descriptors for stdio if available. 772 * @param pipeFd null-ok; pipe for communication back to Zygote. 773 * @param newStderr null-ok; stream to use for stderr until stdio 774 * is reopened. 775 * 776 * @throws ZygoteInit.MethodAndArgsCaller on success to 777 * trampoline to code that invokes static main. 778 */ 779 private void handleChildProc(Arguments parsedArgs, 780 FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) 781 throws ZygoteInit.MethodAndArgsCaller { 782 783 /* 784 * Close the socket, unless we're in "peer wait" mode, in which 785 * case it's used to track the liveness of this process. 786 */ 787 788 if (parsedArgs.peerWait) { 789 try { 790 ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true); 791 sPeerWaitSocket = mSocket; 792 } catch (IOException ex) { 793 Log.e(TAG, "Zygote Child: error setting peer wait " 794 + "socket to be close-on-exec", ex); 795 } 796 } else { 797 closeSocket(); 798 ZygoteInit.closeServerSocket(); 799 } 800 801 if (descriptors != null) { 802 try { 803 ZygoteInit.reopenStdio(descriptors[0], 804 descriptors[1], descriptors[2]); 805 806 for (FileDescriptor fd: descriptors) { 807 IoUtils.closeQuietly(fd); 808 } 809 newStderr = System.err; 810 } catch (IOException ex) { 811 Log.e(TAG, "Error reopening stdio", ex); 812 } 813 } 814 815 if (parsedArgs.niceName != null) { 816 Process.setArgV0(parsedArgs.niceName); 817 } 818 819 if (parsedArgs.runtimeInit) { 820 if (parsedArgs.invokeWith != null) { 821 WrapperInit.execApplication(parsedArgs.invokeWith, 822 parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs); 823 } else { 824 RuntimeInit.zygoteInit(parsedArgs.remainingArgs); 825 } 826 } else { 827 String className; 828 try { 829 className = parsedArgs.remainingArgs[0]; 830 } catch (ArrayIndexOutOfBoundsException ex) { 831 logAndPrintError(newStderr, 832 "Missing required class name argument", null); 833 return; 834 } 835 836 String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1]; 837 System.arraycopy(parsedArgs.remainingArgs, 1, 838 mainArgs, 0, mainArgs.length); 839 840 if (parsedArgs.invokeWith != null) { 841 WrapperInit.execStandalone(parsedArgs.invokeWith, 842 parsedArgs.classpath, className, mainArgs); 843 } else { 844 ClassLoader cloader; 845 if (parsedArgs.classpath != null) { 846 cloader = new PathClassLoader(parsedArgs.classpath, 847 ClassLoader.getSystemClassLoader()); 848 } else { 849 cloader = ClassLoader.getSystemClassLoader(); 850 } 851 852 try { 853 ZygoteInit.invokeStaticMain(cloader, className, mainArgs); 854 } catch (RuntimeException ex) { 855 logAndPrintError(newStderr, "Error starting.", ex); 856 } 857 } 858 } 859 } 860 861 /** 862 * Handles post-fork cleanup of parent proc 863 * 864 * @param pid != 0; pid of child if > 0 or indication of failed fork 865 * if < 0; 866 * @param descriptors null-ok; file descriptors for child's new stdio if 867 * specified. 868 * @param pipeFd null-ok; pipe for communication with child. 869 * @param parsedArgs non-null; zygote args 870 * @return true for "exit command loop" and false for "continue command 871 * loop" 872 */ 873 private boolean handleParentProc(int pid, 874 FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) { 875 876 if (pid > 0) { 877 setChildPgid(pid); 878 } 879 880 if (descriptors != null) { 881 for (FileDescriptor fd: descriptors) { 882 IoUtils.closeQuietly(fd); 883 } 884 } 885 886 if (pipeFd != null && pid > 0) { 887 DataInputStream is = new DataInputStream(new FileInputStream(pipeFd)); 888 int innerPid = -1; 889 try { 890 innerPid = is.readInt(); 891 } catch (IOException ex) { 892 Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex); 893 } finally { 894 try { 895 is.close(); 896 } catch (IOException ex) { 897 } 898 } 899 900 // Ensure that the pid reported by the wrapped process is either the 901 // child process that we forked, or a descendant of it. 902 if (innerPid > 0) { 903 int parentPid = innerPid; 904 while (parentPid > 0 && parentPid != pid) { 905 parentPid = Process.getParentPid(parentPid); 906 } 907 if (parentPid > 0) { 908 Log.i(TAG, "Wrapped process has pid " + innerPid); 909 pid = innerPid; 910 } else { 911 Log.w(TAG, "Wrapped process reported a pid that is not a child of " 912 + "the process that we forked: childPid=" + pid 913 + " innerPid=" + innerPid); 914 } 915 } 916 } 917 918 try { 919 mSocketOutStream.writeInt(pid); 920 } catch (IOException ex) { 921 Log.e(TAG, "Error reading from command socket", ex); 922 return true; 923 } 924 925 /* 926 * If the peer wants to use the socket to wait on the 927 * newly spawned process, then we're all done. 928 */ 929 if (parsedArgs.peerWait) { 930 try { 931 mSocket.close(); 932 } catch (IOException ex) { 933 Log.e(TAG, "Zygote: error closing sockets", ex); 934 } 935 return true; 936 } 937 return false; 938 } 939 940 private void setChildPgid(int pid) { 941 // Try to move the new child into the peer's process group. 942 try { 943 ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); 944 } catch (IOException ex) { 945 // This exception is expected in the case where 946 // the peer is not in our session 947 // TODO get rid of this log message in the case where 948 // getsid(0) != getsid(peer.getPid()) 949 Log.i(TAG, "Zygote: setpgid failed. This is " 950 + "normal if peer is not in our session"); 951 } 952 } 953 954 /** 955 * Logs an error message and prints it to the specified stream, if 956 * provided 957 * 958 * @param newStderr null-ok; a standard error stream 959 * @param message non-null; error message 960 * @param ex null-ok an exception 961 */ 962 private static void logAndPrintError (PrintStream newStderr, 963 String message, Throwable ex) { 964 Log.e(TAG, message, ex); 965 if (newStderr != null) { 966 newStderr.println(message + (ex == null ? "" : ex)); 967 } 968 } 969} 970