1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.os; 18 19 import static android.system.OsConstants.F_SETFD; 20 import static android.system.OsConstants.O_CLOEXEC; 21 import static android.system.OsConstants.STDERR_FILENO; 22 import static android.system.OsConstants.STDIN_FILENO; 23 import static android.system.OsConstants.STDOUT_FILENO; 24 25 import android.net.Credentials; 26 import android.net.LocalSocket; 27 import android.os.Process; 28 import android.os.SELinux; 29 import android.os.SystemProperties; 30 import android.os.Trace; 31 import android.system.ErrnoException; 32 import android.system.Os; 33 import android.util.Log; 34 import dalvik.system.VMRuntime; 35 import java.io.BufferedReader; 36 import java.io.DataInputStream; 37 import java.io.DataOutputStream; 38 import java.io.FileDescriptor; 39 import java.io.FileInputStream; 40 import java.io.FileOutputStream; 41 import java.io.IOException; 42 import java.io.InputStreamReader; 43 import java.io.PrintStream; 44 import java.nio.charset.StandardCharsets; 45 import java.util.ArrayList; 46 import libcore.io.IoUtils; 47 48 /** 49 * A connection that can make spawn requests. 50 */ 51 class 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 */ 67 private static final int MAX_ZYGOTE_ARGC = 1024; 68 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; 80 private final String abiList; 81 82 /** 83 * Constructs instance from connected socket. 84 * 85 * @param socket non-null; connected socket 86 * @param abiList non-null; a list of ABIs this zygote supports. 87 * @throws IOException 88 */ ZygoteConnection(LocalSocket socket, String abiList)89 ZygoteConnection(LocalSocket socket, String abiList) throws IOException { 90 mSocket = socket; 91 this.abiList = abiList; 92 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); 100 101 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 */ getFileDesciptor()114 FileDescriptor getFileDesciptor() { 115 return mSocket.getFileDescriptor(); 116 } 117 118 /** 119 * 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 */ runOnce()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 161 int pid = -1; 162 FileDescriptor childPipeFd = null; 163 FileDescriptor serverPipeFd = null; 164 165 try { 166 parsedArgs = new Arguments(args); 167 168 if (parsedArgs.abiListQuery) { 169 return handleAbiListQuery(); 170 } 171 172 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 } 177 178 applyUidSecurityPolicy(parsedArgs, peer); 179 applyInvokeWithSecurityPolicy(parsedArgs, peer); 180 181 applyDebuggerSystemProperty(parsedArgs); 182 applyInvokeWithSystemProperty(parsedArgs); 183 184 int[][] rlimits = null; 185 186 if (parsedArgs.rlimits != null) { 187 rlimits = parsedArgs.rlimits.toArray(intArray2d); 188 } 189 190 if (parsedArgs.invokeWith != null) { 191 FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC); 192 childPipeFd = pipeFds[1]; 193 serverPipeFd = pipeFds[0]; 194 Os.fcntlInt(childPipeFd, F_SETFD, 0); 195 } 196 197 /** 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 225 pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, 226 parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, 227 parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet, 228 parsedArgs.appDataDir); 229 } catch (ErrnoException ex) { 230 logAndPrintError(newStderr, "Exception creating pipe", ex); 231 } catch (IllegalArgumentException ex) { 232 logAndPrintError(newStderr, "Invalid zygote arguments", ex); 233 } catch (ZygoteSecurityException ex) { 234 logAndPrintError(newStderr, 235 "Zygote security policy prevents request: ", ex); 236 } 237 238 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); 257 } 258 } 259 handleAbiListQuery()260 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 272 /** 273 * Closes socket associated with this connection. 274 */ closeSocket()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 /** 285 * Handles argument parsing for args related to the zygote spawner. 286 * 287 * 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> 302 * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate. 303 * <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> [--] <args for RuntimeInit > 309 * </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 323 /** 324 * From --enable-debugger, --enable-checkjni, --enable-assert, 325 * --enable-safemode, --generate-debug-info and --enable-jni-logging. 326 */ 327 int debugFlags; 328 329 /** From --mount-external */ 330 int mountExternal = Zygote.MOUNT_EXTERNAL_NONE; 331 332 /** from --target-sdk-version. */ 333 int targetSdkVersion; 334 boolean targetSdkVersionSpecified; 335 336 /** from --nice-name */ 337 String niceName; 338 339 /** from --capabilities */ 340 boolean capabilitiesSpecified; 341 long permittedCapabilities; 342 long effectiveCapabilities; 343 344 /** from --seinfo */ 345 boolean seInfoSpecified; 346 String seInfo; 347 348 /** from all --rlimit=r,c,m */ 349 ArrayList<int[]> rlimits; 350 351 /** from --invoke-with */ 352 String invokeWith; 353 354 /** 355 * Any args after and including the first non-option arg 356 * (or after a '--') 357 */ 358 String remainingArgs[]; 359 360 /** 361 * Whether the current arguments constitute an ABI list query. 362 */ 363 boolean abiListQuery; 364 365 /** 366 * The instruction set to use, or null when not important. 367 */ 368 String instructionSet; 369 370 /** 371 * 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 /** 377 * Constructs instance and parses args 378 * @param args zygote command-line args 379 * @throws IllegalArgumentException 380 */ Arguments(String args[])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 */ parseArgs(String args[])393 private void parseArgs(String args[]) 394 throws IllegalArgumentException { 395 int curArg = 0; 396 397 boolean seenRuntimeArgs = false; 398 399 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)); 421 } 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)); 429 } else if (arg.equals("--enable-debugger")) { 430 debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; 431 } else if (arg.equals("--enable-safemode")) { 432 debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; 433 } else if (arg.equals("--enable-checkjni")) { 434 debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; 435 } else if (arg.equals("--generate-debug-info")) { 436 debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; 437 } 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; 441 } else if (arg.equals("--enable-jni-logging")) { 442 debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; 443 } else if (arg.equals("--enable-assert")) { 444 debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; 445 } else if (arg.equals("--runtime-args")) { 446 seenRuntimeArgs = true; 447 } 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); 454 } 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); 491 } 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 } 505 } 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); 522 } else if (arg.equals("--mount-external-default")) { 523 mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT; 524 } 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; 528 } else if (arg.equals("--query-abi-list")) { 529 abiListQuery = true; 530 } else if (arg.startsWith("--instruction-set=")) { 531 instructionSet = arg.substring(arg.indexOf('=') + 1); 532 } else if (arg.startsWith("--app-data-dir=")) { 533 appDataDir = arg.substring(arg.indexOf('=') + 1); 534 } else { 535 break; 536 } 537 } 538 539 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); 550 } 551 } 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 */ readArgumentList()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 588 if (argc > MAX_ZYGOTE_ARGC) { 589 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 /** 605 * uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal 606 * operation. It may also specify any gid and setgroups() list it chooses. 607 * In factory test mode, it may specify any UID. 608 * 609 * @param args non-null; zygote spawner arguments 610 * @param peer non-null; peer credentials 611 * @throws ZygoteSecurityException 612 */ applyUidSecurityPolicy(Arguments args, Credentials peer)613 private static void applyUidSecurityPolicy(Arguments args, Credentials peer) 614 throws ZygoteSecurityException { 615 616 if (peer.getUid() == Process.SYSTEM_UID) { 617 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 */ 623 uidRestricted = !(factoryTest.equals("1") || factoryTest.equals("2")); 624 625 if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) { 626 throw new ZygoteSecurityException( 627 "System UID may not launch process with UID < " 628 + Process.SYSTEM_UID); 629 } 630 } 631 632 // 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 643 /** 644 * Applies debugger system properties to the zygote arguments. 645 * 646 * 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 */ applyDebuggerSystemProperty(Arguments args)652 public static void applyDebuggerSystemProperty(Arguments args) { 653 if ("1".equals(SystemProperties.get("ro.debuggable"))) { 654 args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; 655 } 656 } 657 658 /** 659 * 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 */ applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)671 private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer) 672 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 * 684 * @param args non-null; zygote args 685 */ applyInvokeWithSystemProperty(Arguments args)686 public static void applyInvokeWithSystemProperty(Arguments args) { 687 if (args.invokeWith == null && args.niceName != null) { 688 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); 695 } 696 } 697 args.invokeWith = SystemProperties.get(property); 698 if (args.invokeWith != null && args.invokeWith.length() == 0) { 699 args.invokeWith = null; 700 } 701 } 702 } 703 704 /** 705 * 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. 711 * @param pipeFd null-ok; pipe for communication back to Zygote. 712 * @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 */ handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)718 private void handleChildProc(Arguments parsedArgs, 719 FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) 720 throws ZygoteInit.MethodAndArgsCaller { 721 /** 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 727 closeSocket(); 728 ZygoteInit.closeServerSocket(); 729 730 if (descriptors != null) { 731 try { 732 Os.dup2(descriptors[0], STDIN_FILENO); 733 Os.dup2(descriptors[1], STDOUT_FILENO); 734 Os.dup2(descriptors[2], STDERR_FILENO); 735 736 for (FileDescriptor fd: descriptors) { 737 IoUtils.closeQuietly(fd); 738 } 739 newStderr = System.err; 740 } catch (ErrnoException ex) { 741 Log.e(TAG, "Error reopening stdio", ex); 742 } 743 } 744 745 if (parsedArgs.niceName != null) { 746 Process.setArgV0(parsedArgs.niceName); 747 } 748 749 // End of the postFork event. 750 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 751 if (parsedArgs.invokeWith != null) { 752 WrapperInit.execApplication(parsedArgs.invokeWith, 753 parsedArgs.niceName, parsedArgs.targetSdkVersion, 754 VMRuntime.getCurrentInstructionSet(), 755 pipeFd, parsedArgs.remainingArgs); 756 } else { 757 RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, 758 parsedArgs.remainingArgs, null /* classLoader */); 759 } 760 } 761 762 /** 763 * Handles post-fork cleanup of parent proc 764 * 765 * @param pid != 0; pid of child if > 0 or indication of failed fork 766 * if < 0; 767 * @param descriptors null-ok; file descriptors for child's new stdio if 768 * specified. 769 * @param pipeFd null-ok; pipe for communication with child. 770 * @param parsedArgs non-null; zygote args 771 * @return true for "exit command loop" and false for "continue command 772 * loop" 773 */ handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs)774 private boolean handleParentProc(int pid, 775 FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) { 776 777 if (pid > 0) { 778 setChildPgid(pid); 779 } 780 781 if (descriptors != null) { 782 for (FileDescriptor fd: descriptors) { 783 IoUtils.closeQuietly(fd); 784 } 785 } 786 787 boolean usingWrapper = false; 788 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) { 799 } 800 } 801 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; 812 usingWrapper = true; 813 } 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 } 819 } 820 821 try { 822 mSocketOutStream.writeInt(pid); 823 mSocketOutStream.writeBoolean(usingWrapper); 824 } catch (IOException ex) { 825 Log.e(TAG, "Error writing to command socket", ex); 826 return true; 827 } 828 829 return false; 830 } 831 setChildPgid(int pid)832 private void setChildPgid(int pid) { 833 // Try to move the new child into the peer's process group. 834 try { 835 Os.setpgid(pid, Os.getpgid(peer.getPid())); 836 } catch (ErrnoException ex) { 837 // 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 846 /** 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 */ logAndPrintError(PrintStream newStderr, String message, Throwable ex)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 } 862