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 * Constructs instance and parses args 247 * 248 * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count. 249 */ ZygoteArguments(ZygoteCommandBuffer args, int argCount)250 private ZygoteArguments(ZygoteCommandBuffer args, int argCount) 251 throws IllegalArgumentException, EOFException { 252 parseArgs(args, argCount); 253 } 254 255 /** 256 * Return a new ZygoteArguments reflecting the contents of the given ZygoteCommandBuffer. Return 257 * null if the ZygoteCommandBuffer was positioned at EOF. Assumes the buffer is initially 258 * positioned at the beginning of the command. 259 */ getInstance(ZygoteCommandBuffer args)260 public static ZygoteArguments getInstance(ZygoteCommandBuffer args) 261 throws IllegalArgumentException, EOFException { 262 int argCount = args.getCount(); 263 return argCount == 0 ? null : new ZygoteArguments(args, argCount); 264 } 265 266 /** 267 * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and 268 * "--setgid=") and creates an array containing the remaining args. Return false if we were 269 * at EOF. 270 * 271 * Per security review bug #1112214, duplicate args are disallowed in critical cases to make 272 * injection harder. 273 */ parseArgs(ZygoteCommandBuffer args, int argCount)274 private void parseArgs(ZygoteCommandBuffer args, int argCount) 275 throws IllegalArgumentException, EOFException { 276 /* 277 * See android.os.ZygoteProcess.zygoteSendArgsAndGetResult() 278 * Presently the wire format to the zygote process is: 279 * a) a count of arguments (argc, in essence) 280 * b) a number of newline-separated argument strings equal to count 281 * 282 * After the zygote process reads these it will write the pid of 283 * the child or -1 on failure. 284 */ 285 286 String unprocessedArg = null; 287 int curArg = 0; // Index of arg 288 boolean seenRuntimeArgs = false; 289 boolean expectRuntimeArgs = true; 290 291 for ( /* curArg */ ; curArg < argCount; ++curArg) { 292 String arg = args.nextArg(); 293 294 if (arg.equals("--")) { 295 curArg++; 296 break; 297 } else if (arg.startsWith("--setuid=")) { 298 if (mUidSpecified) { 299 throw new IllegalArgumentException( 300 "Duplicate arg specified"); 301 } 302 mUidSpecified = true; 303 mUid = Integer.parseInt(getAssignmentValue(arg)); 304 } else if (arg.startsWith("--setgid=")) { 305 if (mGidSpecified) { 306 throw new IllegalArgumentException( 307 "Duplicate arg specified"); 308 } 309 mGidSpecified = true; 310 mGid = Integer.parseInt(getAssignmentValue(arg)); 311 } else if (arg.startsWith("--target-sdk-version=")) { 312 if (mTargetSdkVersionSpecified) { 313 throw new IllegalArgumentException( 314 "Duplicate target-sdk-version specified"); 315 } 316 mTargetSdkVersionSpecified = true; 317 mTargetSdkVersion = Integer.parseInt(getAssignmentValue(arg)); 318 } else if (arg.equals("--runtime-args")) { 319 seenRuntimeArgs = true; 320 } else if (arg.startsWith("--runtime-flags=")) { 321 mRuntimeFlags = Integer.parseInt(getAssignmentValue(arg)); 322 } else if (arg.startsWith("--seinfo=")) { 323 if (mSeInfoSpecified) { 324 throw new IllegalArgumentException( 325 "Duplicate arg specified"); 326 } 327 mSeInfoSpecified = true; 328 mSeInfo = getAssignmentValue(arg); 329 } else if (arg.startsWith("--capabilities=")) { 330 if (mCapabilitiesSpecified) { 331 throw new IllegalArgumentException( 332 "Duplicate arg specified"); 333 } 334 mCapabilitiesSpecified = true; 335 String capString = getAssignmentValue(arg); 336 337 String[] capStrings = capString.split(",", 2); 338 339 if (capStrings.length == 1) { 340 mEffectiveCapabilities = Long.decode(capStrings[0]); 341 mPermittedCapabilities = mEffectiveCapabilities; 342 } else { 343 mPermittedCapabilities = Long.decode(capStrings[0]); 344 mEffectiveCapabilities = Long.decode(capStrings[1]); 345 } 346 } else if (arg.startsWith("--rlimit=")) { 347 // Duplicate --rlimit arguments are specifically allowed. 348 String[] limitStrings = getAssignmentList(arg); 349 350 if (limitStrings.length != 3) { 351 throw new IllegalArgumentException( 352 "--rlimit= should have 3 comma-delimited ints"); 353 } 354 int[] rlimitTuple = new int[limitStrings.length]; 355 356 for (int i = 0; i < limitStrings.length; i++) { 357 rlimitTuple[i] = Integer.parseInt(limitStrings[i]); 358 } 359 360 if (mRLimits == null) { 361 mRLimits = new ArrayList<>(); 362 } 363 364 mRLimits.add(rlimitTuple); 365 } else if (arg.startsWith("--setgroups=")) { 366 if (mGids != null) { 367 throw new IllegalArgumentException( 368 "Duplicate arg specified"); 369 } 370 371 String[] params = getAssignmentList(arg); 372 373 mGids = new int[params.length]; 374 375 for (int i = params.length - 1; i >= 0; i--) { 376 mGids[i] = Integer.parseInt(params[i]); 377 } 378 } else if (arg.equals("--invoke-with")) { 379 if (mInvokeWith != null) { 380 throw new IllegalArgumentException( 381 "Duplicate arg specified"); 382 } 383 try { 384 ++curArg; 385 mInvokeWith = args.nextArg(); 386 } catch (IndexOutOfBoundsException ex) { 387 throw new IllegalArgumentException( 388 "--invoke-with requires argument"); 389 } 390 } else if (arg.startsWith("--nice-name=")) { 391 if (mNiceName != null) { 392 throw new IllegalArgumentException( 393 "Duplicate arg specified"); 394 } 395 mNiceName = getAssignmentValue(arg); 396 } else if (arg.equals("--mount-external-default")) { 397 mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT; 398 } else if (arg.equals("--mount-external-installer")) { 399 mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER; 400 } else if (arg.equals("--mount-external-pass-through")) { 401 mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH; 402 } else if (arg.equals("--mount-external-android-writable")) { 403 mMountExternal = Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; 404 } else if (arg.equals("--query-abi-list")) { 405 mAbiListQuery = true; 406 } else if (arg.equals("--get-pid")) { 407 mPidQuery = true; 408 } else if (arg.equals("--boot-completed")) { 409 mBootCompleted = true; 410 } else if (arg.startsWith("--instruction-set=")) { 411 mInstructionSet = getAssignmentValue(arg); 412 } else if (arg.startsWith("--app-data-dir=")) { 413 mAppDataDir = getAssignmentValue(arg); 414 } else if (arg.equals("--preload-app")) { 415 ++curArg; 416 mPreloadApp = args.nextArg(); 417 } else if (arg.equals("--preload-package")) { 418 curArg += 4; 419 mPreloadPackage = args.nextArg(); 420 mPreloadPackageLibs = args.nextArg(); 421 mPreloadPackageLibFileName = args.nextArg(); 422 mPreloadPackageCacheKey = args.nextArg(); 423 } else if (arg.equals("--preload-default")) { 424 mPreloadDefault = true; 425 expectRuntimeArgs = false; 426 } else if (arg.equals("--start-child-zygote")) { 427 mStartChildZygote = true; 428 } else if (arg.equals("--set-api-denylist-exemptions")) { 429 // consume all remaining args; this is a stand-alone command, never included 430 // with the regular fork command. 431 mApiDenylistExemptions = new String[argCount - curArg - 1]; 432 ++curArg; 433 for (int i = 0; curArg < argCount; ++curArg, ++i) { 434 mApiDenylistExemptions[i] = args.nextArg(); 435 } 436 expectRuntimeArgs = false; 437 } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) { 438 String rateStr = getAssignmentValue(arg); 439 try { 440 mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr); 441 } catch (NumberFormatException nfe) { 442 throw new IllegalArgumentException( 443 "Invalid log sampling rate: " + rateStr, nfe); 444 } 445 expectRuntimeArgs = false; 446 } else if (arg.startsWith("--hidden-api-statslog-sampling-rate=")) { 447 String rateStr = getAssignmentValue(arg); 448 try { 449 mHiddenApiAccessStatslogSampleRate = Integer.parseInt(rateStr); 450 } catch (NumberFormatException nfe) { 451 throw new IllegalArgumentException( 452 "Invalid statslog sampling rate: " + rateStr, nfe); 453 } 454 expectRuntimeArgs = false; 455 } else if (arg.startsWith("--package-name=")) { 456 if (mPackageName != null) { 457 throw new IllegalArgumentException("Duplicate arg specified"); 458 } 459 mPackageName = getAssignmentValue(arg); 460 } else if (arg.startsWith("--usap-pool-enabled=")) { 461 mUsapPoolStatusSpecified = true; 462 mUsapPoolEnabled = Boolean.parseBoolean(getAssignmentValue(arg)); 463 expectRuntimeArgs = false; 464 } else if (arg.startsWith(Zygote.START_AS_TOP_APP_ARG)) { 465 mIsTopApp = true; 466 } else if (arg.startsWith("--disabled-compat-changes=")) { 467 if (mDisabledCompatChanges != null) { 468 throw new IllegalArgumentException("Duplicate arg specified"); 469 } 470 final String[] params = getAssignmentList(arg); 471 final int length = params.length; 472 mDisabledCompatChanges = new long[length]; 473 for (int i = 0; i < length; i++) { 474 mDisabledCompatChanges[i] = Long.parseLong(params[i]); 475 } 476 } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) { 477 mPkgDataInfoList = getAssignmentList(arg); 478 } else if (arg.startsWith(Zygote.ALLOWLISTED_DATA_INFO_MAP)) { 479 mAllowlistedDataInfoList = getAssignmentList(arg); 480 } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) { 481 mBindMountAppStorageDirs = true; 482 } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) { 483 mBindMountAppDataDirs = true; 484 } else { 485 unprocessedArg = arg; 486 break; 487 } 488 } 489 // curArg is the index of the first unprocessed argument. That argument is either referenced 490 // by unprocessedArg or not read yet. 491 492 if (mBootCompleted) { 493 if (argCount > curArg) { 494 throw new IllegalArgumentException("Unexpected arguments after --boot-completed"); 495 } 496 } else if (mAbiListQuery || mPidQuery) { 497 if (argCount > curArg) { 498 throw new IllegalArgumentException("Unexpected arguments after --query-abi-list."); 499 } 500 } else if (mPreloadPackage != null) { 501 if (argCount > curArg) { 502 throw new IllegalArgumentException( 503 "Unexpected arguments after --preload-package."); 504 } 505 } else if (mPreloadApp != null) { 506 if (argCount > curArg) { 507 throw new IllegalArgumentException( 508 "Unexpected arguments after --preload-app."); 509 } 510 } else if (expectRuntimeArgs) { 511 if (!seenRuntimeArgs) { 512 throw new IllegalArgumentException("Unexpected argument : " 513 + (unprocessedArg == null ? args.nextArg() : unprocessedArg)); 514 } 515 516 mRemainingArgs = new String[argCount - curArg]; 517 int i = 0; 518 if (unprocessedArg != null) { 519 mRemainingArgs[0] = unprocessedArg; 520 ++i; 521 } 522 for (; i < argCount - curArg; ++i) { 523 mRemainingArgs[i] = args.nextArg(); 524 } 525 } 526 527 if (mStartChildZygote) { 528 boolean seenChildSocketArg = false; 529 for (String arg : mRemainingArgs) { 530 if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) { 531 seenChildSocketArg = true; 532 break; 533 } 534 } 535 if (!seenChildSocketArg) { 536 throw new IllegalArgumentException("--start-child-zygote specified " 537 + "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG); 538 } 539 } 540 } 541 getAssignmentValue(String arg)542 private static String getAssignmentValue(String arg) { 543 return arg.substring(arg.indexOf('=') + 1); 544 } 545 getAssignmentList(String arg)546 private static String[] getAssignmentList(String arg) { 547 return getAssignmentValue(arg).split(","); 548 } 549 } 550