blob: 2564f6b4d610fb975dc0963ed19c15d72e72b101 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Handles argument parsing for args related to the zygote spawner.
*
* Current recognized args:
* <ul>
* <li> --setuid=<i>uid of child process, defaults to 0</i>
* <li> --setgid=<i>gid of child process, defaults to 0</i>
* <li> --setgroups=<i>comma-separated list of supplimentary gid's</i>
* <li> --capabilities=<i>a pair of comma-separated integer strings
* indicating Linux capabilities(2) set for child. The first string
* represents the <code>permitted</code> set, and the second the
* <code>effective</code> set. Precede each with 0 or
* 0x for octal or hexidecimal value. If unspecified, both default to 0.
* This parameter is only applied if the uid of the new process will
* be non-0. </i>
* <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
* <code>r</code> is the resource, <code>c</code> and <code>m</code>
* are the settings for current and max value.</i>
* <li> --instruction-set=<i>instruction-set-string</i> which instruction set to use/emulate.
* <li> --nice-name=<i>nice name to appear in ps</i>
* <li> --package-name=<i>package name this process belongs to</i>
* <li> --runtime-args indicates that the remaining arg list should
* be handed off to com.android.internal.os.RuntimeInit, rather than
* processed directly.
* Android runtime startup (eg, Binder initialization) is also eschewed.
* <li> [--] &lt;args for RuntimeInit &gt;
* </ul>
*/
class ZygoteArguments {
/**
* from --setuid
*/
int mUid = 0;
boolean mUidSpecified;
/**
* from --setgid
*/
int mGid = 0;
boolean mGidSpecified;
/**
* from --setgroups
*/
int[] mGids;
/**
* From --runtime-flags.
*/
int mRuntimeFlags;
/**
* From --mount-external
*/
int mMountExternal = Zygote.MOUNT_EXTERNAL_NONE;
/**
* from --target-sdk-version.
*/
int mTargetSdkVersion;
boolean mTargetSdkVersionSpecified;
/**
* from --nice-name
*/
String mNiceName;
/**
* from --capabilities
*/
boolean mCapabilitiesSpecified;
long mPermittedCapabilities;
long mEffectiveCapabilities;
/**
* from --seinfo
*/
boolean mSeInfoSpecified;
String mSeInfo;
/**
*
*/
boolean mUsapPoolEnabled;
boolean mUsapPoolStatusSpecified = false;
/**
* from all --rlimit=r,c,m
*/
ArrayList<int[]> mRLimits;
/**
* from --invoke-with
*/
String mInvokeWith;
/** from --package-name */
String mPackageName;
/**
* Any args after and including the first non-option arg (or after a '--')
*/
String[] mRemainingArgs;
/**
* Whether the current arguments constitute an ABI list query.
*/
boolean mAbiListQuery;
/**
* The instruction set to use, or null when not important.
*/
String mInstructionSet;
/**
* The app data directory. May be null, e.g., for the system server. Note that this might not be
* reliable in the case of process-sharing apps.
*/
String mAppDataDir;
/**
* The APK path of the package to preload, when using --preload-package.
*/
String mPreloadPackage;
/**
* A Base64 string representing a serialize ApplicationInfo Parcel,
when using --preload-app.
*/
String mPreloadApp;
/**
* The native library path of the package to preload, when using --preload-package.
*/
String mPreloadPackageLibs;
/**
* The filename of the native library to preload, when using --preload-package.
*/
String mPreloadPackageLibFileName;
/**
* The cache key under which to enter the preloaded package into the classloader cache, when
* using --preload-package.
*/
String mPreloadPackageCacheKey;
/**
* Whether this is a request to start preloading the default resources and classes. This
* argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started
* with --enable-lazy-preload).
*/
boolean mPreloadDefault;
/**
* Whether this is a request to start a zygote process as a child of this zygote. Set with
* --start-child-zygote. The remaining arguments must include the CHILD_ZYGOTE_SOCKET_NAME_ARG
* flag to indicate the abstract socket name that should be used for communication.
*/
boolean mStartChildZygote;
/**
* Whether the current arguments constitute a request for the zygote's PID.
*/
boolean mPidQuery;
/**
* Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time, or
* when they change, via --set-api-blacklist-exemptions.
*/
String[] mApiBlacklistExemptions;
/**
* Sampling rate for logging hidden API accesses to the event log. This is sent to the
* pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
*/
int mHiddenApiAccessLogSampleRate = -1;
/**
* Sampling rate for logging hidden API accesses to statslog. This is sent to the
* pre-forked zygote at boot time, or when it changes, via --hidden-api-statslog-sampling-rate.
*/
int mHiddenApiAccessStatslogSampleRate = -1;
/**
* Constructs instance and parses args
*
* @param args zygote command-line args
*/
ZygoteArguments(String[] args) throws IllegalArgumentException {
parseArgs(args);
}
/**
* Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and
* "--setgid=") and creates an array containing the remaining args.
*
* Per security review bug #1112214, duplicate args are disallowed in critical cases to make
* injection harder.
*/
private void parseArgs(String[] args) throws IllegalArgumentException {
/*
* See android.os.ZygoteProcess.zygoteSendArgsAndGetResult()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
* the child or -1 on failure.
*/
int curArg = 0;
boolean seenRuntimeArgs = false;
boolean expectRuntimeArgs = true;
for ( /* curArg */ ; curArg < args.length; curArg++) {
String arg = args[curArg];
if (arg.equals("--")) {
curArg++;
break;
} else if (arg.startsWith("--setuid=")) {
if (mUidSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
mUidSpecified = true;
mUid = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
} else if (arg.startsWith("--setgid=")) {
if (mGidSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
mGidSpecified = true;
mGid = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
} else if (arg.startsWith("--target-sdk-version=")) {
if (mTargetSdkVersionSpecified) {
throw new IllegalArgumentException(
"Duplicate target-sdk-version specified");
}
mTargetSdkVersionSpecified = true;
mTargetSdkVersion = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
} else if (arg.equals("--runtime-args")) {
seenRuntimeArgs = true;
} else if (arg.startsWith("--runtime-flags=")) {
mRuntimeFlags = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
} else if (arg.startsWith("--seinfo=")) {
if (mSeInfoSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
mSeInfoSpecified = true;
mSeInfo = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--capabilities=")) {
if (mCapabilitiesSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
mCapabilitiesSpecified = true;
String capString = arg.substring(arg.indexOf('=') + 1);
String[] capStrings = capString.split(",", 2);
if (capStrings.length == 1) {
mEffectiveCapabilities = Long.decode(capStrings[0]);
mPermittedCapabilities = mEffectiveCapabilities;
} else {
mPermittedCapabilities = Long.decode(capStrings[0]);
mEffectiveCapabilities = Long.decode(capStrings[1]);
}
} else if (arg.startsWith("--rlimit=")) {
// Duplicate --rlimit arguments are specifically allowed.
String[] limitStrings = arg.substring(arg.indexOf('=') + 1).split(",");
if (limitStrings.length != 3) {
throw new IllegalArgumentException(
"--rlimit= should have 3 comma-delimited ints");
}
int[] rlimitTuple = new int[limitStrings.length];
for (int i = 0; i < limitStrings.length; i++) {
rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
}
if (mRLimits == null) {
mRLimits = new ArrayList();
}
mRLimits.add(rlimitTuple);
} else if (arg.startsWith("--setgroups=")) {
if (mGids != null) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
String[] params = arg.substring(arg.indexOf('=') + 1).split(",");
mGids = new int[params.length];
for (int i = params.length - 1; i >= 0; i--) {
mGids[i] = Integer.parseInt(params[i]);
}
} else if (arg.equals("--invoke-with")) {
if (mInvokeWith != null) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
try {
mInvokeWith = args[++curArg];
} catch (IndexOutOfBoundsException ex) {
throw new IllegalArgumentException(
"--invoke-with requires argument");
}
} else if (arg.startsWith("--nice-name=")) {
if (mNiceName != null) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
mNiceName = arg.substring(arg.indexOf('=') + 1);
} else if (arg.equals("--mount-external-default")) {
mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
} else if (arg.equals("--mount-external-read")) {
mMountExternal = Zygote.MOUNT_EXTERNAL_READ;
} else if (arg.equals("--mount-external-write")) {
mMountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
} else if (arg.equals("--mount-external-full")) {
mMountExternal = Zygote.MOUNT_EXTERNAL_FULL;
} else if (arg.equals("--mount-external-installer")) {
mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
} else if (arg.equals("--mount-external-legacy")) {
mMountExternal = Zygote.MOUNT_EXTERNAL_LEGACY;
} else if (arg.equals("--query-abi-list")) {
mAbiListQuery = true;
} else if (arg.equals("--get-pid")) {
mPidQuery = true;
} else if (arg.startsWith("--instruction-set=")) {
mInstructionSet = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--app-data-dir=")) {
mAppDataDir = arg.substring(arg.indexOf('=') + 1);
} else if (arg.equals("--preload-app")) {
mPreloadApp = args[++curArg];
} else if (arg.equals("--preload-package")) {
mPreloadPackage = args[++curArg];
mPreloadPackageLibs = args[++curArg];
mPreloadPackageLibFileName = args[++curArg];
mPreloadPackageCacheKey = args[++curArg];
} else if (arg.equals("--preload-default")) {
mPreloadDefault = true;
expectRuntimeArgs = false;
} else if (arg.equals("--start-child-zygote")) {
mStartChildZygote = true;
} else if (arg.equals("--set-api-blacklist-exemptions")) {
// consume all remaining args; this is a stand-alone command, never included
// with the regular fork command.
mApiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
curArg = args.length;
expectRuntimeArgs = false;
} else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
String rateStr = arg.substring(arg.indexOf('=') + 1);
try {
mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(
"Invalid log sampling rate: " + rateStr, nfe);
}
expectRuntimeArgs = false;
} else if (arg.startsWith("--hidden-api-statslog-sampling-rate=")) {
String rateStr = arg.substring(arg.indexOf('=') + 1);
try {
mHiddenApiAccessStatslogSampleRate = Integer.parseInt(rateStr);
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(
"Invalid statslog sampling rate: " + rateStr, nfe);
}
expectRuntimeArgs = false;
} else if (arg.startsWith("--package-name=")) {
if (mPackageName != null) {
throw new IllegalArgumentException("Duplicate arg specified");
}
mPackageName = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--usap-pool-enabled=")) {
mUsapPoolStatusSpecified = true;
mUsapPoolEnabled = Boolean.parseBoolean(arg.substring(arg.indexOf('=') + 1));
expectRuntimeArgs = false;
} else {
break;
}
}
if (mAbiListQuery || mPidQuery) {
if (args.length - curArg > 0) {
throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
}
} else if (mPreloadPackage != null) {
if (args.length - curArg > 0) {
throw new IllegalArgumentException(
"Unexpected arguments after --preload-package.");
}
} else if (mPreloadApp != null) {
if (args.length - curArg > 0) {
throw new IllegalArgumentException(
"Unexpected arguments after --preload-app.");
}
} else if (expectRuntimeArgs) {
if (!seenRuntimeArgs) {
throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
}
mRemainingArgs = new String[args.length - curArg];
System.arraycopy(args, curArg, mRemainingArgs, 0, mRemainingArgs.length);
}
if (mStartChildZygote) {
boolean seenChildSocketArg = false;
for (String arg : mRemainingArgs) {
if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
seenChildSocketArg = true;
break;
}
}
if (!seenChildSocketArg) {
throw new IllegalArgumentException("--start-child-zygote specified "
+ "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
}
}
}
}