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