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