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