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