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 java.io.EOFException; 20 import java.util.ArrayList; 21 22 /** 23 * Handles argument parsing for args related to the zygote spawner. 24 * 25 * Current recognized args: 26 * <ul> 27 * <li> --setuid=<i>uid of child process, defaults to 0</i> 28 * <li> --setgid=<i>gid of child process, defaults to 0</i> 29 * <li> --setgroups=<i>comma-separated list of supplimentary gid's</i> 30 * <li> --capabilities=<i>a pair of comma-separated integer strings 31 * indicating Linux capabilities(2) set for child. The first string 32 * represents the <code>permitted</code> set, and the second the 33 * <code>effective</code> set. Precede each with 0 or 34 * 0x for octal or hexidecimal value. If unspecified, both default to 0. 35 * This parameter is only applied if the uid of the new process will 36 * be non-0. </i> 37 * <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call. 38 * <code>r</code> is the resource, <code>c</code> and <code>m</code> 39 * are the settings for current and max value.</i> 40 * <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate. 41 * <li> --nice-name=<i>nice name to appear in ps</i> 42 * <li> --package-name=<i>package name this process belongs to</i> 43 * <li> --runtime-args indicates that the remaining arg list should 44 * be handed off to com.android.internal.os.RuntimeInit, rather than 45 * processed directly. 46 * Android runtime startup (eg, Binder initialization) is also eschewed. 47 * <li> [--] <args for RuntimeInit > 48 * </ul> 49 */ 50 class ZygoteArguments { 51 52 /** 53 * from --setuid 54 */ 55 int mUid = 0; 56 boolean mUidSpecified; 57 58 /** 59 * from --setgid 60 */ 61 int mGid = 0; 62 boolean mGidSpecified; 63 64 /** 65 * from --setgroups 66 */ 67 int[] mGids; 68 69 /** 70 * From --runtime-flags. 71 */ 72 int mRuntimeFlags; 73 74 /** 75 * From --mount-external 76 */ 77 int mMountExternal = Zygote.MOUNT_EXTERNAL_NONE; 78 79 /** 80 * from --target-sdk-version. 81 */ 82 private boolean mTargetSdkVersionSpecified; 83 int mTargetSdkVersion; 84 85 /** 86 * from --nice-name 87 */ 88 String mNiceName; 89 90 /** 91 * from --capabilities 92 */ 93 private boolean mCapabilitiesSpecified; 94 long mPermittedCapabilities; 95 long mEffectiveCapabilities; 96 97 /** 98 * from --seinfo 99 */ 100 private boolean mSeInfoSpecified; 101 String mSeInfo; 102 103 /** 104 * 105 */ 106 boolean mUsapPoolEnabled; 107 boolean mUsapPoolStatusSpecified = false; 108 109 /** 110 * from all --rlimit=r,c,m 111 */ 112 ArrayList<int[]> mRLimits; 113 114 /** 115 * from --invoke-with 116 */ 117 String mInvokeWith; 118 119 /** from --package-name */ 120 String mPackageName; 121 122 /** 123 * Any args after and including the first non-option arg (or after a '--') 124 */ 125 String[] mRemainingArgs; 126 127 /** 128 * Whether the current arguments constitute an ABI list query. 129 */ 130 boolean mAbiListQuery; 131 132 /** 133 * The instruction set to use, or null when not important. 134 */ 135 String mInstructionSet; 136 137 /** 138 * The app data directory. May be null, e.g., for the system server. Note that this might not be 139 * reliable in the case of process-sharing apps. 140 */ 141 String mAppDataDir; 142 143 /** 144 * The APK path of the package to preload, when using --preload-package. 145 */ 146 String mPreloadPackage; 147 148 /** 149 * A Base64 string representing a serialize ApplicationInfo Parcel, 150 when using --preload-app. 151 */ 152 String mPreloadApp; 153 154 /** 155 * The native library path of the package to preload, when using --preload-package. 156 */ 157 String mPreloadPackageLibs; 158 159 /** 160 * The filename of the native library to preload, when using --preload-package. 161 */ 162 String mPreloadPackageLibFileName; 163 164 /** 165 * The cache key under which to enter the preloaded package into the classloader cache, when 166 * using --preload-package. 167 */ 168 String mPreloadPackageCacheKey; 169 170 /** 171 * Whether this is a request to start preloading the default resources and classes. This 172 * argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started 173 * with --enable-lazy-preload). 174 */ 175 boolean mPreloadDefault; 176 177 /** 178 * Whether this is a request to start a zygote process as a child of this zygote. Set with 179 * --start-child-zygote. The remaining arguments must include the CHILD_ZYGOTE_SOCKET_NAME_ARG 180 * flag to indicate the abstract socket name that should be used for communication. 181 */ 182 boolean mStartChildZygote; 183 184 /** 185 * Whether the current arguments constitute a request for the zygote's PID. 186 */ 187 boolean mPidQuery; 188 189 /** 190 * Whether the current arguments constitute a notification that boot completed. 191 */ 192 boolean mBootCompleted; 193 194 /** 195 * Exemptions from API deny-listing. These are sent to the pre-forked zygote at boot time, or 196 * when they change, via --set-api-denylist-exemptions. 197 */ 198 String[] mApiDenylistExemptions; 199 200 /** 201 * Sampling rate for logging hidden API accesses to the event log. This is sent to the 202 * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate. 203 */ 204 int mHiddenApiAccessLogSampleRate = -1; 205 206 /** 207 * Sampling rate for logging hidden API accesses to statslog. This is sent to the 208 * pre-forked zygote at boot time, or when it changes, via --hidden-api-statslog-sampling-rate. 209 */ 210 int mHiddenApiAccessStatslogSampleRate = -1; 211 212 /** 213 * @see Zygote#START_AS_TOP_APP_ARG 214 */ 215 boolean mIsTopApp; 216 217 /** 218 * A set of disabled app compatibility changes for the running app. From 219 * --disabled-compat-changes. 220 */ 221 long[] mDisabledCompatChanges = null; 222 223 /** 224 * A list that stores all related packages and its data info: volume uuid and inode. 225 * Null if it does need to do app data isolation. 226 */ 227 String[] mPkgDataInfoList; 228 229 /** 230 * A list that stores all allowlisted app data info: volume uuid and inode. 231 * Null if it does need to do app data isolation. 232 */ 233 String[] mAllowlistedDataInfoList; 234 235 /** 236 * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS 237 */ 238 boolean mBindMountAppStorageDirs; 239 240 /** 241 * @see Zygote#BIND_MOUNT_APP_DATA_DIRS 242 */ 243 boolean mBindMountAppDataDirs; 244 245 /** 246 * @see Zygote#BIND_MOUNT_SYSPROP_OVERRIDES 247 */ 248 boolean mBindMountSyspropOverrides; 249 250 /** 251 * Constructs instance and parses args 252 * 253 * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count. 254 */ ZygoteArguments(ZygoteCommandBuffer args, int argCount)255 private ZygoteArguments(ZygoteCommandBuffer args, int argCount) 256 throws IllegalArgumentException, EOFException { 257 parseArgs(args, argCount); 258 } 259 260 /** 261 * Return a new ZygoteArguments reflecting the contents of the given ZygoteCommandBuffer. Return 262 * null if the ZygoteCommandBuffer was positioned at EOF. Assumes the buffer is initially 263 * positioned at the beginning of the command. 264 */ getInstance(ZygoteCommandBuffer args)265 public static ZygoteArguments getInstance(ZygoteCommandBuffer args) 266 throws IllegalArgumentException, EOFException { 267 int argCount = args.getCount(); 268 return argCount == 0 ? null : new ZygoteArguments(args, argCount); 269 } 270 271 /** 272 * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and 273 * "--setgid=") and creates an array containing the remaining args. Return false if we were 274 * at EOF. 275 * 276 * Per security review bug #1112214, duplicate args are disallowed in critical cases to make 277 * injection harder. 278 */ parseArgs(ZygoteCommandBuffer args, int argCount)279 private void parseArgs(ZygoteCommandBuffer args, int argCount) 280 throws IllegalArgumentException, EOFException { 281 /* 282 * See android.os.ZygoteProcess.zygoteSendArgsAndGetResult() 283 * Presently the wire format to the zygote process is: 284 * a) a count of arguments (argc, in essence) 285 * b) a number of newline-separated argument strings equal to count 286 * 287 * After the zygote process reads these it will write the pid of 288 * the child or -1 on failure. 289 */ 290 291 String unprocessedArg = null; 292 int curArg = 0; // Index of arg 293 boolean seenRuntimeArgs = false; 294 boolean expectRuntimeArgs = true; 295 296 for ( /* curArg */ ; curArg < argCount; ++curArg) { 297 String arg = args.nextArg(); 298 299 if (arg.equals("--")) { 300 curArg++; 301 break; 302 } else if (arg.startsWith("--setuid=")) { 303 if (mUidSpecified) { 304 throw new IllegalArgumentException( 305 "Duplicate arg specified"); 306 } 307 mUidSpecified = true; 308 mUid = Integer.parseInt(getAssignmentValue(arg)); 309 } else if (arg.startsWith("--setgid=")) { 310 if (mGidSpecified) { 311 throw new IllegalArgumentException( 312 "Duplicate arg specified"); 313 } 314 mGidSpecified = true; 315 mGid = Integer.parseInt(getAssignmentValue(arg)); 316 } else if (arg.startsWith("--target-sdk-version=")) { 317 if (mTargetSdkVersionSpecified) { 318 throw new IllegalArgumentException( 319 "Duplicate target-sdk-version specified"); 320 } 321 mTargetSdkVersionSpecified = true; 322 mTargetSdkVersion = Integer.parseInt(getAssignmentValue(arg)); 323 } else if (arg.equals("--runtime-args")) { 324 seenRuntimeArgs = true; 325 } else if (arg.startsWith("--runtime-flags=")) { 326 mRuntimeFlags = Integer.parseInt(getAssignmentValue(arg)); 327 } else if (arg.startsWith("--seinfo=")) { 328 if (mSeInfoSpecified) { 329 throw new IllegalArgumentException( 330 "Duplicate arg specified"); 331 } 332 mSeInfoSpecified = true; 333 mSeInfo = getAssignmentValue(arg); 334 } else if (arg.startsWith("--capabilities=")) { 335 if (mCapabilitiesSpecified) { 336 throw new IllegalArgumentException( 337 "Duplicate arg specified"); 338 } 339 mCapabilitiesSpecified = true; 340 String capString = getAssignmentValue(arg); 341 342 String[] capStrings = capString.split(",", 2); 343 344 if (capStrings.length == 1) { 345 mEffectiveCapabilities = Long.decode(capStrings[0]); 346 mPermittedCapabilities = mEffectiveCapabilities; 347 } else { 348 mPermittedCapabilities = Long.decode(capStrings[0]); 349 mEffectiveCapabilities = Long.decode(capStrings[1]); 350 } 351 } else if (arg.startsWith("--rlimit=")) { 352 // Duplicate --rlimit arguments are specifically allowed. 353 String[] limitStrings = getAssignmentList(arg); 354 355 if (limitStrings.length != 3) { 356 throw new IllegalArgumentException( 357 "--rlimit= should have 3 comma-delimited ints"); 358 } 359 int[] rlimitTuple = new int[limitStrings.length]; 360 361 for (int i = 0; i < limitStrings.length; i++) { 362 rlimitTuple[i] = Integer.parseInt(limitStrings[i]); 363 } 364 365 if (mRLimits == null) { 366 mRLimits = new ArrayList<>(); 367 } 368 369 mRLimits.add(rlimitTuple); 370 } else if (arg.startsWith("--setgroups=")) { 371 if (mGids != null) { 372 throw new IllegalArgumentException( 373 "Duplicate arg specified"); 374 } 375 376 String[] params = getAssignmentList(arg); 377 378 mGids = new int[params.length]; 379 380 for (int i = params.length - 1; i >= 0; i--) { 381 mGids[i] = Integer.parseInt(params[i]); 382 } 383 } else if (arg.equals("--invoke-with")) { 384 if (mInvokeWith != null) { 385 throw new IllegalArgumentException( 386 "Duplicate arg specified"); 387 } 388 try { 389 ++curArg; 390 mInvokeWith = args.nextArg(); 391 } catch (IndexOutOfBoundsException ex) { 392 throw new IllegalArgumentException( 393 "--invoke-with requires argument"); 394 } 395 } else if (arg.startsWith("--nice-name=")) { 396 if (mNiceName != null) { 397 throw new IllegalArgumentException( 398 "Duplicate arg specified"); 399 } 400 mNiceName = getAssignmentValue(arg); 401 } else if (arg.equals("--mount-external-default")) { 402 mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT; 403 } else if (arg.equals("--mount-external-installer")) { 404 mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER; 405 } else if (arg.equals("--mount-external-pass-through")) { 406 mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH; 407 } else if (arg.equals("--mount-external-android-writable")) { 408 mMountExternal = Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; 409 } else if (arg.equals("--query-abi-list")) { 410 mAbiListQuery = true; 411 } else if (arg.equals("--get-pid")) { 412 mPidQuery = true; 413 } else if (arg.equals("--boot-completed")) { 414 mBootCompleted = true; 415 } else if (arg.startsWith("--instruction-set=")) { 416 mInstructionSet = getAssignmentValue(arg); 417 } else if (arg.startsWith("--app-data-dir=")) { 418 mAppDataDir = getAssignmentValue(arg); 419 } else if (arg.equals("--preload-app")) { 420 ++curArg; 421 mPreloadApp = args.nextArg(); 422 } else if (arg.equals("--preload-package")) { 423 curArg += 4; 424 mPreloadPackage = args.nextArg(); 425 mPreloadPackageLibs = args.nextArg(); 426 mPreloadPackageLibFileName = args.nextArg(); 427 mPreloadPackageCacheKey = args.nextArg(); 428 } else if (arg.equals("--preload-default")) { 429 mPreloadDefault = true; 430 expectRuntimeArgs = false; 431 } else if (arg.equals("--start-child-zygote")) { 432 mStartChildZygote = true; 433 } else if (arg.equals("--set-api-denylist-exemptions")) { 434 // consume all remaining args; this is a stand-alone command, never included 435 // with the regular fork command. 436 mApiDenylistExemptions = new String[argCount - curArg - 1]; 437 ++curArg; 438 for (int i = 0; curArg < argCount; ++curArg, ++i) { 439 mApiDenylistExemptions[i] = args.nextArg(); 440 } 441 expectRuntimeArgs = false; 442 } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) { 443 String rateStr = getAssignmentValue(arg); 444 try { 445 mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr); 446 } catch (NumberFormatException nfe) { 447 throw new IllegalArgumentException( 448 "Invalid log sampling rate: " + rateStr, nfe); 449 } 450 expectRuntimeArgs = false; 451 } else if (arg.startsWith("--hidden-api-statslog-sampling-rate=")) { 452 String rateStr = getAssignmentValue(arg); 453 try { 454 mHiddenApiAccessStatslogSampleRate = Integer.parseInt(rateStr); 455 } catch (NumberFormatException nfe) { 456 throw new IllegalArgumentException( 457 "Invalid statslog sampling rate: " + rateStr, nfe); 458 } 459 expectRuntimeArgs = false; 460 } else if (arg.startsWith("--package-name=")) { 461 if (mPackageName != null) { 462 throw new IllegalArgumentException("Duplicate arg specified"); 463 } 464 mPackageName = getAssignmentValue(arg); 465 } else if (arg.startsWith("--usap-pool-enabled=")) { 466 mUsapPoolStatusSpecified = true; 467 mUsapPoolEnabled = Boolean.parseBoolean(getAssignmentValue(arg)); 468 expectRuntimeArgs = false; 469 } else if (arg.startsWith(Zygote.START_AS_TOP_APP_ARG)) { 470 mIsTopApp = true; 471 } else if (arg.startsWith("--disabled-compat-changes=")) { 472 if (mDisabledCompatChanges != null) { 473 throw new IllegalArgumentException("Duplicate arg specified"); 474 } 475 final String[] params = getAssignmentList(arg); 476 final int length = params.length; 477 mDisabledCompatChanges = new long[length]; 478 for (int i = 0; i < length; i++) { 479 mDisabledCompatChanges[i] = Long.parseLong(params[i]); 480 } 481 } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) { 482 mPkgDataInfoList = getAssignmentList(arg); 483 } else if (arg.startsWith(Zygote.ALLOWLISTED_DATA_INFO_MAP)) { 484 mAllowlistedDataInfoList = getAssignmentList(arg); 485 } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) { 486 mBindMountAppStorageDirs = true; 487 } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) { 488 mBindMountAppDataDirs = true; 489 } else if (arg.equals(Zygote.BIND_MOUNT_SYSPROP_OVERRIDES)) { 490 mBindMountSyspropOverrides = true; 491 } else { 492 unprocessedArg = arg; 493 break; 494 } 495 } 496 // curArg is the index of the first unprocessed argument. That argument is either referenced 497 // by unprocessedArg or not read yet. 498 499 if (mBootCompleted) { 500 if (argCount > curArg) { 501 throw new IllegalArgumentException("Unexpected arguments after --boot-completed"); 502 } 503 } else if (mAbiListQuery || mPidQuery) { 504 if (argCount > curArg) { 505 throw new IllegalArgumentException("Unexpected arguments after --query-abi-list."); 506 } 507 } else if (mPreloadPackage != null) { 508 if (argCount > curArg) { 509 throw new IllegalArgumentException( 510 "Unexpected arguments after --preload-package."); 511 } 512 } else if (mPreloadApp != null) { 513 if (argCount > curArg) { 514 throw new IllegalArgumentException( 515 "Unexpected arguments after --preload-app."); 516 } 517 } else if (expectRuntimeArgs) { 518 if (!seenRuntimeArgs) { 519 throw new IllegalArgumentException("Unexpected argument : " 520 + (unprocessedArg == null ? args.nextArg() : unprocessedArg)); 521 } 522 523 mRemainingArgs = new String[argCount - curArg]; 524 int i = 0; 525 if (unprocessedArg != null) { 526 mRemainingArgs[0] = unprocessedArg; 527 ++i; 528 } 529 for (; i < argCount - curArg; ++i) { 530 mRemainingArgs[i] = args.nextArg(); 531 } 532 } 533 534 if (mStartChildZygote) { 535 boolean seenChildSocketArg = false; 536 for (String arg : mRemainingArgs) { 537 if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) { 538 seenChildSocketArg = true; 539 break; 540 } 541 } 542 if (!seenChildSocketArg) { 543 throw new IllegalArgumentException("--start-child-zygote specified " 544 + "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG); 545 } 546 } 547 } 548 getAssignmentValue(String arg)549 private static String getAssignmentValue(String arg) { 550 return arg.substring(arg.indexOf('=') + 1); 551 } 552 getAssignmentList(String arg)553 private static String[] getAssignmentList(String arg) { 554 return getAssignmentValue(arg).split(","); 555 } 556 } 557