• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> [--] &lt;args for RuntimeInit &gt;
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