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