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