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 * A Base64 string representing a serialize ApplicationInfo Parcel, 145 when using --preload-app. 146 */ 147 String mPreloadApp; 148 149 /** 150 * Whether this is a request to start preloading the default resources and classes. This 151 * argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started 152 * with --enable-lazy-preload). 153 */ 154 boolean mPreloadDefault; 155 156 /** 157 * Whether this is a request to start a zygote process as a child of this zygote. Set with 158 * --start-child-zygote. The remaining arguments must include the CHILD_ZYGOTE_SOCKET_NAME_ARG 159 * flag to indicate the abstract socket name that should be used for communication. 160 */ 161 boolean mStartChildZygote; 162 163 /** 164 * Whether the current arguments constitute a request for the zygote's PID. 165 */ 166 boolean mPidQuery; 167 168 /** 169 * Whether the current arguments constitute a notification that boot completed. 170 */ 171 boolean mBootCompleted; 172 173 /** 174 * Exemptions from API deny-listing. These are sent to the pre-forked zygote at boot time, or 175 * when they change, via --set-api-denylist-exemptions. 176 */ 177 String[] mApiDenylistExemptions; 178 179 /** 180 * Sampling rate for logging hidden API accesses to the event log. This is sent to the 181 * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate. 182 */ 183 int mHiddenApiAccessLogSampleRate = -1; 184 185 /** 186 * Sampling rate for logging hidden API accesses to statslog. This is sent to the 187 * pre-forked zygote at boot time, or when it changes, via --hidden-api-statslog-sampling-rate. 188 */ 189 int mHiddenApiAccessStatslogSampleRate = -1; 190 191 /** 192 * @see Zygote#START_AS_TOP_APP_ARG 193 */ 194 boolean mIsTopApp; 195 196 /** 197 * A set of disabled app compatibility changes for the running app. From 198 * --disabled-compat-changes. 199 */ 200 long[] mDisabledCompatChanges = null; 201 202 /** 203 * A list that stores all related packages and its data info: volume uuid and inode. 204 * Null if it does need to do app data isolation. 205 */ 206 String[] mPkgDataInfoList; 207 208 /** 209 * A list that stores all allowlisted app data info: volume uuid and inode. 210 * Null if it does need to do app data isolation. 211 */ 212 String[] mAllowlistedDataInfoList; 213 214 /** 215 * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS 216 */ 217 boolean mBindMountAppStorageDirs; 218 219 /** 220 * @see Zygote#BIND_MOUNT_APP_DATA_DIRS 221 */ 222 boolean mBindMountAppDataDirs; 223 224 /** 225 * @see Zygote#BIND_MOUNT_SYSPROP_OVERRIDES 226 */ 227 boolean mBindMountSyspropOverrides; 228 229 /** 230 * Constructs instance and parses args 231 * 232 * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count. 233 */ ZygoteArguments(ZygoteCommandBuffer args, int argCount)234 private ZygoteArguments(ZygoteCommandBuffer args, int argCount) 235 throws IllegalArgumentException, EOFException { 236 parseArgs(args, argCount); 237 } 238 239 /** 240 * Return a new ZygoteArguments reflecting the contents of the given ZygoteCommandBuffer. Return 241 * null if the ZygoteCommandBuffer was positioned at EOF. Assumes the buffer is initially 242 * positioned at the beginning of the command. 243 */ getInstance(ZygoteCommandBuffer args)244 public static ZygoteArguments getInstance(ZygoteCommandBuffer args) 245 throws IllegalArgumentException, EOFException { 246 int argCount = args.getCount(); 247 return argCount == 0 ? null : new ZygoteArguments(args, argCount); 248 } 249 250 /** 251 * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and 252 * "--setgid=") and creates an array containing the remaining args. Return false if we were 253 * at EOF. 254 * 255 * Per security review bug #1112214, duplicate args are disallowed in critical cases to make 256 * injection harder. 257 */ parseArgs(ZygoteCommandBuffer args, int argCount)258 private void parseArgs(ZygoteCommandBuffer args, int argCount) 259 throws IllegalArgumentException, EOFException { 260 /* 261 * See android.os.ZygoteProcess.zygoteSendArgsAndGetResult() 262 * Presently the wire format to the zygote process is: 263 * a) a count of arguments (argc, in essence) 264 * b) a number of newline-separated argument strings equal to count 265 * 266 * After the zygote process reads these it will write the pid of 267 * the child or -1 on failure. 268 */ 269 270 String unprocessedArg = null; 271 int curArg = 0; // Index of arg 272 boolean seenRuntimeArgs = false; 273 boolean expectRuntimeArgs = true; 274 275 for ( /* curArg */ ; curArg < argCount; ++curArg) { 276 String arg = args.nextArg(); 277 278 if (arg.equals("--")) { 279 curArg++; 280 break; 281 } else if (arg.startsWith("--setuid=")) { 282 if (mUidSpecified) { 283 throw new IllegalArgumentException( 284 "Duplicate arg specified"); 285 } 286 mUidSpecified = true; 287 mUid = Integer.parseInt(getAssignmentValue(arg)); 288 } else if (arg.startsWith("--setgid=")) { 289 if (mGidSpecified) { 290 throw new IllegalArgumentException( 291 "Duplicate arg specified"); 292 } 293 mGidSpecified = true; 294 mGid = Integer.parseInt(getAssignmentValue(arg)); 295 } else if (arg.startsWith("--target-sdk-version=")) { 296 if (mTargetSdkVersionSpecified) { 297 throw new IllegalArgumentException( 298 "Duplicate target-sdk-version specified"); 299 } 300 mTargetSdkVersionSpecified = true; 301 mTargetSdkVersion = Integer.parseInt(getAssignmentValue(arg)); 302 } else if (arg.equals("--runtime-args")) { 303 seenRuntimeArgs = true; 304 } else if (arg.startsWith("--runtime-flags=")) { 305 mRuntimeFlags = Integer.parseInt(getAssignmentValue(arg)); 306 } else if (arg.startsWith("--seinfo=")) { 307 if (mSeInfoSpecified) { 308 throw new IllegalArgumentException( 309 "Duplicate arg specified"); 310 } 311 mSeInfoSpecified = true; 312 mSeInfo = getAssignmentValue(arg); 313 } else if (arg.startsWith("--capabilities=")) { 314 if (mCapabilitiesSpecified) { 315 throw new IllegalArgumentException( 316 "Duplicate arg specified"); 317 } 318 mCapabilitiesSpecified = true; 319 String capString = getAssignmentValue(arg); 320 321 String[] capStrings = capString.split(",", 2); 322 323 if (capStrings.length == 1) { 324 mEffectiveCapabilities = Long.decode(capStrings[0]); 325 mPermittedCapabilities = mEffectiveCapabilities; 326 } else { 327 mPermittedCapabilities = Long.decode(capStrings[0]); 328 mEffectiveCapabilities = Long.decode(capStrings[1]); 329 } 330 } else if (arg.startsWith("--rlimit=")) { 331 // Duplicate --rlimit arguments are specifically allowed. 332 String[] limitStrings = getAssignmentList(arg); 333 334 if (limitStrings.length != 3) { 335 throw new IllegalArgumentException( 336 "--rlimit= should have 3 comma-delimited ints"); 337 } 338 int[] rlimitTuple = new int[limitStrings.length]; 339 340 for (int i = 0; i < limitStrings.length; i++) { 341 rlimitTuple[i] = Integer.parseInt(limitStrings[i]); 342 } 343 344 if (mRLimits == null) { 345 mRLimits = new ArrayList<>(); 346 } 347 348 mRLimits.add(rlimitTuple); 349 } else if (arg.startsWith("--setgroups=")) { 350 if (mGids != null) { 351 throw new IllegalArgumentException( 352 "Duplicate arg specified"); 353 } 354 355 String[] params = getAssignmentList(arg); 356 357 mGids = new int[params.length]; 358 359 for (int i = params.length - 1; i >= 0; i--) { 360 mGids[i] = Integer.parseInt(params[i]); 361 } 362 } else if (arg.equals("--invoke-with")) { 363 if (mInvokeWith != null) { 364 throw new IllegalArgumentException( 365 "Duplicate arg specified"); 366 } 367 try { 368 ++curArg; 369 mInvokeWith = args.nextArg(); 370 } catch (IndexOutOfBoundsException ex) { 371 throw new IllegalArgumentException( 372 "--invoke-with requires argument"); 373 } 374 } else if (arg.startsWith("--nice-name=")) { 375 if (mNiceName != null) { 376 throw new IllegalArgumentException( 377 "Duplicate arg specified"); 378 } 379 mNiceName = getAssignmentValue(arg); 380 } else if (arg.equals("--mount-external-default")) { 381 mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT; 382 } else if (arg.equals("--mount-external-installer")) { 383 mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER; 384 } else if (arg.equals("--mount-external-pass-through")) { 385 mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH; 386 } else if (arg.equals("--mount-external-android-writable")) { 387 mMountExternal = Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; 388 } else if (arg.equals("--query-abi-list")) { 389 mAbiListQuery = true; 390 } else if (arg.equals("--get-pid")) { 391 mPidQuery = true; 392 } else if (arg.equals("--boot-completed")) { 393 mBootCompleted = true; 394 } else if (arg.startsWith("--instruction-set=")) { 395 mInstructionSet = getAssignmentValue(arg); 396 } else if (arg.startsWith("--app-data-dir=")) { 397 mAppDataDir = getAssignmentValue(arg); 398 } else if (arg.equals("--preload-app")) { 399 ++curArg; 400 mPreloadApp = args.nextArg(); 401 } else if (arg.equals("--preload-default")) { 402 mPreloadDefault = true; 403 expectRuntimeArgs = false; 404 } else if (arg.equals("--start-child-zygote")) { 405 mStartChildZygote = true; 406 } else if (arg.equals("--set-api-denylist-exemptions")) { 407 // consume all remaining args; this is a stand-alone command, never included 408 // with the regular fork command. 409 mApiDenylistExemptions = new String[argCount - curArg - 1]; 410 ++curArg; 411 for (int i = 0; curArg < argCount; ++curArg, ++i) { 412 mApiDenylistExemptions[i] = args.nextArg(); 413 } 414 expectRuntimeArgs = false; 415 } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) { 416 String rateStr = getAssignmentValue(arg); 417 try { 418 mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr); 419 } catch (NumberFormatException nfe) { 420 throw new IllegalArgumentException( 421 "Invalid log sampling rate: " + rateStr, nfe); 422 } 423 expectRuntimeArgs = false; 424 } else if (arg.startsWith("--hidden-api-statslog-sampling-rate=")) { 425 String rateStr = getAssignmentValue(arg); 426 try { 427 mHiddenApiAccessStatslogSampleRate = Integer.parseInt(rateStr); 428 } catch (NumberFormatException nfe) { 429 throw new IllegalArgumentException( 430 "Invalid statslog sampling rate: " + rateStr, nfe); 431 } 432 expectRuntimeArgs = false; 433 } else if (arg.startsWith("--package-name=")) { 434 if (mPackageName != null) { 435 throw new IllegalArgumentException("Duplicate arg specified"); 436 } 437 mPackageName = getAssignmentValue(arg); 438 } else if (arg.startsWith("--usap-pool-enabled=")) { 439 mUsapPoolStatusSpecified = true; 440 mUsapPoolEnabled = Boolean.parseBoolean(getAssignmentValue(arg)); 441 expectRuntimeArgs = false; 442 } else if (arg.startsWith(Zygote.START_AS_TOP_APP_ARG)) { 443 mIsTopApp = true; 444 } else if (arg.startsWith("--disabled-compat-changes=")) { 445 if (mDisabledCompatChanges != null) { 446 throw new IllegalArgumentException("Duplicate arg specified"); 447 } 448 final String[] params = getAssignmentList(arg); 449 final int length = params.length; 450 mDisabledCompatChanges = new long[length]; 451 for (int i = 0; i < length; i++) { 452 mDisabledCompatChanges[i] = Long.parseLong(params[i]); 453 } 454 } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) { 455 mPkgDataInfoList = getAssignmentList(arg); 456 } else if (arg.startsWith(Zygote.ALLOWLISTED_DATA_INFO_MAP)) { 457 mAllowlistedDataInfoList = getAssignmentList(arg); 458 } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) { 459 mBindMountAppStorageDirs = true; 460 } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) { 461 mBindMountAppDataDirs = true; 462 } else if (arg.equals(Zygote.BIND_MOUNT_SYSPROP_OVERRIDES)) { 463 mBindMountSyspropOverrides = true; 464 } else { 465 unprocessedArg = arg; 466 break; 467 } 468 } 469 // curArg is the index of the first unprocessed argument. That argument is either referenced 470 // by unprocessedArg or not read yet. 471 472 if (mBootCompleted) { 473 if (argCount > curArg) { 474 throw new IllegalArgumentException("Unexpected arguments after --boot-completed"); 475 } 476 } else if (mAbiListQuery || mPidQuery) { 477 if (argCount > curArg) { 478 throw new IllegalArgumentException("Unexpected arguments after --query-abi-list."); 479 } 480 } else if (mPreloadApp != null) { 481 if (argCount > curArg) { 482 throw new IllegalArgumentException( 483 "Unexpected arguments after --preload-app."); 484 } 485 } else if (expectRuntimeArgs) { 486 if (!seenRuntimeArgs) { 487 throw new IllegalArgumentException("Unexpected argument : " 488 + (unprocessedArg == null ? args.nextArg() : unprocessedArg)); 489 } 490 491 mRemainingArgs = new String[argCount - curArg]; 492 int i = 0; 493 if (unprocessedArg != null) { 494 mRemainingArgs[0] = unprocessedArg; 495 ++i; 496 } 497 for (; i < argCount - curArg; ++i) { 498 mRemainingArgs[i] = args.nextArg(); 499 } 500 } 501 502 if (mStartChildZygote) { 503 boolean seenChildSocketArg = false; 504 for (String arg : mRemainingArgs) { 505 if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) { 506 seenChildSocketArg = true; 507 break; 508 } 509 } 510 if (!seenChildSocketArg) { 511 throw new IllegalArgumentException("--start-child-zygote specified " 512 + "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG); 513 } 514 } 515 } 516 getAssignmentValue(String arg)517 private static String getAssignmentValue(String arg) { 518 return arg.substring(arg.indexOf('=') + 1); 519 } 520 getAssignmentList(String arg)521 private static String[] getAssignmentList(String arg) { 522 return getAssignmentValue(arg).split(","); 523 } 524 } 525