• 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.io.EOFException;
20 import java.util.ArrayList;
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     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      * A Base64 string representing a serialize ApplicationInfo Parcel,
145      when using --preload-app.
146      */
147     String mPreloadApp;
148 
149     /**
150      * Whether this is a request to start preloading the default resources and classes. This
151      * argument only makes sense when the zygote is in lazy preload mode (i.e, when it's started
152      * with --enable-lazy-preload).
153      */
154     boolean mPreloadDefault;
155 
156     /**
157      * Whether this is a request to start a zygote process as a child of this zygote. Set with
158      * --start-child-zygote. The remaining arguments must include the CHILD_ZYGOTE_SOCKET_NAME_ARG
159      * flag to indicate the abstract socket name that should be used for communication.
160      */
161     boolean mStartChildZygote;
162 
163     /**
164      * Whether the current arguments constitute a request for the zygote's PID.
165      */
166     boolean mPidQuery;
167 
168     /**
169      * Whether the current arguments constitute a notification that boot completed.
170      */
171     boolean mBootCompleted;
172 
173     /**
174      * Exemptions from API deny-listing. These are sent to the pre-forked zygote at boot time, or
175      * when they change, via --set-api-denylist-exemptions.
176      */
177     String[] mApiDenylistExemptions;
178 
179     /**
180      * Sampling rate for logging hidden API accesses to the event log. This is sent to the
181      * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
182      */
183     int mHiddenApiAccessLogSampleRate = -1;
184 
185     /**
186      * Sampling rate for logging hidden API accesses to statslog. This is sent to the
187      * pre-forked zygote at boot time, or when it changes, via --hidden-api-statslog-sampling-rate.
188      */
189     int mHiddenApiAccessStatslogSampleRate = -1;
190 
191     /**
192      * @see Zygote#START_AS_TOP_APP_ARG
193      */
194     boolean mIsTopApp;
195 
196     /**
197      * A set of disabled app compatibility changes for the running app. From
198      * --disabled-compat-changes.
199      */
200     long[] mDisabledCompatChanges = null;
201 
202     /**
203      * A list that stores all related packages and its data info: volume uuid and inode.
204      * Null if it does need to do app data isolation.
205      */
206     String[] mPkgDataInfoList;
207 
208     /**
209      * A list that stores all allowlisted app data info: volume uuid and inode.
210      * Null if it does need to do app data isolation.
211      */
212     String[] mAllowlistedDataInfoList;
213 
214     /**
215      * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
216      */
217     boolean mBindMountAppStorageDirs;
218 
219     /**
220      * @see Zygote#BIND_MOUNT_APP_DATA_DIRS
221      */
222     boolean mBindMountAppDataDirs;
223 
224     /**
225      * @see Zygote#BIND_MOUNT_SYSPROP_OVERRIDES
226      */
227     boolean mBindMountSyspropOverrides;
228 
229     /**
230      * Constructs instance and parses args
231      *
232      * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count.
233      */
ZygoteArguments(ZygoteCommandBuffer args, int argCount)234     private ZygoteArguments(ZygoteCommandBuffer args, int argCount)
235             throws IllegalArgumentException, EOFException {
236         parseArgs(args, argCount);
237     }
238 
239     /**
240      * Return a new ZygoteArguments reflecting the contents of the given ZygoteCommandBuffer. Return
241      * null if the ZygoteCommandBuffer was positioned at EOF. Assumes the buffer is initially
242      * positioned at the beginning of the command.
243      */
getInstance(ZygoteCommandBuffer args)244     public static ZygoteArguments getInstance(ZygoteCommandBuffer args)
245             throws IllegalArgumentException, EOFException {
246         int argCount = args.getCount();
247         return argCount == 0 ? null : new ZygoteArguments(args, argCount);
248     }
249 
250     /**
251      * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and
252      * "--setgid=") and creates an array containing the remaining args. Return false if we were
253      * at EOF.
254      *
255      * Per security review bug #1112214, duplicate args are disallowed in critical cases to make
256      * injection harder.
257      */
parseArgs(ZygoteCommandBuffer args, int argCount)258     private void parseArgs(ZygoteCommandBuffer args, int argCount)
259             throws IllegalArgumentException, EOFException {
260         /*
261          * See android.os.ZygoteProcess.zygoteSendArgsAndGetResult()
262          * Presently the wire format to the zygote process is:
263          * a) a count of arguments (argc, in essence)
264          * b) a number of newline-separated argument strings equal to count
265          *
266          * After the zygote process reads these it will write the pid of
267          * the child or -1 on failure.
268          */
269 
270         String unprocessedArg = null;
271         int curArg = 0;  // Index of arg
272         boolean seenRuntimeArgs = false;
273         boolean expectRuntimeArgs = true;
274 
275         for ( /* curArg */ ; curArg < argCount; ++curArg) {
276             String arg = args.nextArg();
277 
278             if (arg.equals("--")) {
279                 curArg++;
280                 break;
281             } else if (arg.startsWith("--setuid=")) {
282                 if (mUidSpecified) {
283                     throw new IllegalArgumentException(
284                         "Duplicate arg specified");
285                 }
286                 mUidSpecified = true;
287                 mUid = Integer.parseInt(getAssignmentValue(arg));
288             } else if (arg.startsWith("--setgid=")) {
289                 if (mGidSpecified) {
290                     throw new IllegalArgumentException(
291                         "Duplicate arg specified");
292                 }
293                 mGidSpecified = true;
294                 mGid = Integer.parseInt(getAssignmentValue(arg));
295             } else if (arg.startsWith("--target-sdk-version=")) {
296                 if (mTargetSdkVersionSpecified) {
297                     throw new IllegalArgumentException(
298                         "Duplicate target-sdk-version specified");
299                 }
300                 mTargetSdkVersionSpecified = true;
301                 mTargetSdkVersion = Integer.parseInt(getAssignmentValue(arg));
302             } else if (arg.equals("--runtime-args")) {
303                 seenRuntimeArgs = true;
304             } else if (arg.startsWith("--runtime-flags=")) {
305                 mRuntimeFlags = Integer.parseInt(getAssignmentValue(arg));
306             } else if (arg.startsWith("--seinfo=")) {
307                 if (mSeInfoSpecified) {
308                     throw new IllegalArgumentException(
309                         "Duplicate arg specified");
310                 }
311                 mSeInfoSpecified = true;
312                 mSeInfo = getAssignmentValue(arg);
313             } else if (arg.startsWith("--capabilities=")) {
314                 if (mCapabilitiesSpecified) {
315                     throw new IllegalArgumentException(
316                         "Duplicate arg specified");
317                 }
318                 mCapabilitiesSpecified = true;
319                 String capString = getAssignmentValue(arg);
320 
321                 String[] capStrings = capString.split(",", 2);
322 
323                 if (capStrings.length == 1) {
324                     mEffectiveCapabilities = Long.decode(capStrings[0]);
325                     mPermittedCapabilities = mEffectiveCapabilities;
326                 } else {
327                     mPermittedCapabilities = Long.decode(capStrings[0]);
328                     mEffectiveCapabilities = Long.decode(capStrings[1]);
329                 }
330             } else if (arg.startsWith("--rlimit=")) {
331                 // Duplicate --rlimit arguments are specifically allowed.
332                 String[] limitStrings = getAssignmentList(arg);
333 
334                 if (limitStrings.length != 3) {
335                     throw new IllegalArgumentException(
336                         "--rlimit= should have 3 comma-delimited ints");
337                 }
338                 int[] rlimitTuple = new int[limitStrings.length];
339 
340                 for (int i = 0; i < limitStrings.length; i++) {
341                     rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
342                 }
343 
344                 if (mRLimits == null) {
345                     mRLimits = new ArrayList<>();
346                 }
347 
348                 mRLimits.add(rlimitTuple);
349             } else if (arg.startsWith("--setgroups=")) {
350                 if (mGids != null) {
351                     throw new IllegalArgumentException(
352                         "Duplicate arg specified");
353                 }
354 
355                 String[] params = getAssignmentList(arg);
356 
357                 mGids = new int[params.length];
358 
359                 for (int i = params.length - 1; i >= 0; i--) {
360                     mGids[i] = Integer.parseInt(params[i]);
361                 }
362             } else if (arg.equals("--invoke-with")) {
363                 if (mInvokeWith != null) {
364                     throw new IllegalArgumentException(
365                         "Duplicate arg specified");
366                 }
367                 try {
368                     ++curArg;
369                     mInvokeWith = args.nextArg();
370                 } catch (IndexOutOfBoundsException ex) {
371                     throw new IllegalArgumentException(
372                         "--invoke-with requires argument");
373                 }
374             } else if (arg.startsWith("--nice-name=")) {
375                 if (mNiceName != null) {
376                     throw new IllegalArgumentException(
377                         "Duplicate arg specified");
378                 }
379                 mNiceName = getAssignmentValue(arg);
380             } else if (arg.equals("--mount-external-default")) {
381                 mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
382             } else if (arg.equals("--mount-external-installer")) {
383                 mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
384             } else if (arg.equals("--mount-external-pass-through")) {
385                 mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
386             } else if (arg.equals("--mount-external-android-writable")) {
387                 mMountExternal = Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
388             } else if (arg.equals("--query-abi-list")) {
389                 mAbiListQuery = true;
390             } else if (arg.equals("--get-pid")) {
391                 mPidQuery = true;
392             } else if (arg.equals("--boot-completed")) {
393                 mBootCompleted = true;
394             } else if (arg.startsWith("--instruction-set=")) {
395                 mInstructionSet = getAssignmentValue(arg);
396             } else if (arg.startsWith("--app-data-dir=")) {
397                 mAppDataDir = getAssignmentValue(arg);
398             } else if (arg.equals("--preload-app")) {
399                 ++curArg;
400                 mPreloadApp = args.nextArg();
401             } else if (arg.equals("--preload-default")) {
402                 mPreloadDefault = true;
403                 expectRuntimeArgs = false;
404             } else if (arg.equals("--start-child-zygote")) {
405                 mStartChildZygote = true;
406             } else if (arg.equals("--set-api-denylist-exemptions")) {
407                 // consume all remaining args; this is a stand-alone command, never included
408                 // with the regular fork command.
409                 mApiDenylistExemptions = new String[argCount - curArg - 1];
410                 ++curArg;
411                 for (int i = 0; curArg < argCount; ++curArg, ++i) {
412                     mApiDenylistExemptions[i] = args.nextArg();
413                 }
414                 expectRuntimeArgs = false;
415             } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
416                 String rateStr = getAssignmentValue(arg);
417                 try {
418                     mHiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
419                 } catch (NumberFormatException nfe) {
420                     throw new IllegalArgumentException(
421                         "Invalid log sampling rate: " + rateStr, nfe);
422                 }
423                 expectRuntimeArgs = false;
424             } else if (arg.startsWith("--hidden-api-statslog-sampling-rate=")) {
425                 String rateStr = getAssignmentValue(arg);
426                 try {
427                     mHiddenApiAccessStatslogSampleRate = Integer.parseInt(rateStr);
428                 } catch (NumberFormatException nfe) {
429                     throw new IllegalArgumentException(
430                         "Invalid statslog sampling rate: " + rateStr, nfe);
431                 }
432                 expectRuntimeArgs = false;
433             } else if (arg.startsWith("--package-name=")) {
434                 if (mPackageName != null) {
435                     throw new IllegalArgumentException("Duplicate arg specified");
436                 }
437                 mPackageName = getAssignmentValue(arg);
438             } else if (arg.startsWith("--usap-pool-enabled=")) {
439                 mUsapPoolStatusSpecified = true;
440                 mUsapPoolEnabled = Boolean.parseBoolean(getAssignmentValue(arg));
441                 expectRuntimeArgs = false;
442             } else if (arg.startsWith(Zygote.START_AS_TOP_APP_ARG)) {
443                 mIsTopApp = true;
444             } else if (arg.startsWith("--disabled-compat-changes=")) {
445                 if (mDisabledCompatChanges != null) {
446                     throw new IllegalArgumentException("Duplicate arg specified");
447                 }
448                 final String[] params = getAssignmentList(arg);
449                 final int length = params.length;
450                 mDisabledCompatChanges = new long[length];
451                 for (int i = 0; i < length; i++) {
452                     mDisabledCompatChanges[i] = Long.parseLong(params[i]);
453                 }
454             } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
455                 mPkgDataInfoList = getAssignmentList(arg);
456             } else if (arg.startsWith(Zygote.ALLOWLISTED_DATA_INFO_MAP)) {
457                 mAllowlistedDataInfoList = getAssignmentList(arg);
458             } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
459                 mBindMountAppStorageDirs = true;
460             } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
461                 mBindMountAppDataDirs = true;
462             } else if (arg.equals(Zygote.BIND_MOUNT_SYSPROP_OVERRIDES)) {
463                 mBindMountSyspropOverrides = true;
464             } else {
465                 unprocessedArg = arg;
466                 break;
467             }
468         }
469         // curArg is the index of the first unprocessed argument. That argument is either referenced
470         // by unprocessedArg or not read yet.
471 
472         if (mBootCompleted) {
473             if (argCount > curArg) {
474                 throw new IllegalArgumentException("Unexpected arguments after --boot-completed");
475             }
476         } else if (mAbiListQuery || mPidQuery) {
477             if (argCount > curArg) {
478                 throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
479             }
480         } else if (mPreloadApp != null) {
481             if (argCount > curArg) {
482                 throw new IllegalArgumentException(
483                     "Unexpected arguments after --preload-app.");
484             }
485         } else if (expectRuntimeArgs) {
486             if (!seenRuntimeArgs) {
487                 throw new IllegalArgumentException("Unexpected argument : "
488                     + (unprocessedArg == null ? args.nextArg() : unprocessedArg));
489             }
490 
491             mRemainingArgs = new String[argCount - curArg];
492             int i = 0;
493             if (unprocessedArg != null) {
494                 mRemainingArgs[0] = unprocessedArg;
495                 ++i;
496             }
497             for (; i < argCount - curArg; ++i) {
498                 mRemainingArgs[i] = args.nextArg();
499             }
500         }
501 
502         if (mStartChildZygote) {
503             boolean seenChildSocketArg = false;
504             for (String arg : mRemainingArgs) {
505                 if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
506                     seenChildSocketArg = true;
507                     break;
508                 }
509             }
510             if (!seenChildSocketArg) {
511                 throw new IllegalArgumentException("--start-child-zygote specified "
512                         + "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
513             }
514         }
515     }
516 
getAssignmentValue(String arg)517     private static String getAssignmentValue(String arg) {
518         return arg.substring(arg.indexOf('=') + 1);
519     }
520 
getAssignmentList(String arg)521     private static String[] getAssignmentList(String arg) {
522         return getAssignmentValue(arg).split(",");
523     }
524 }
525