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