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