Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 682b479a authored by Chris Wailes's avatar Chris Wailes
Browse files

Refactored ZygoteConnection.Arguments to ZygoteArguments.

This commit moves the ZygoteConnection.Arguments class into its own file
and re-names it to ZygoteArgumens.  Doing this also required small
changes to files that used ZygoteConnection.Arguments.  In turn, this
required some changes to make the files conform to the Frameworks style
guide.

Highlights include:
* Moving ZygoteConnection.Arguments to ZygoteArguments
* Moving helper functions from ZygoteConnection to Zygote
* Re-named member variables in the ZygoteArguments class
* Removed unused imports.

Topic: zygote-prefork
Test: make & flash & launch apps & check log for messages
Bug: 68253328
Change-Id: Ideb414c87a92020128a644147949ef4f4133ae33
Merged-In: Ideb414c87a92020128a644147949ef4f4133ae33
parent cd92db25
Loading
Loading
Loading
Loading
+154 −0
Original line number Diff line number Diff line
@@ -16,13 +16,23 @@

package com.android.internal.os;

import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;

import android.net.Credentials;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;

import dalvik.system.ZygoteHooks;

import java.io.BufferedReader;
import java.io.IOException;

/** @hide */
public final class Zygote {
    /*
@@ -91,6 +101,9 @@ public final class Zygote {
     */
    public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";

    /** a prototype instance for a future List.toArray() */
    protected static final int[][] INT_ARRAY_2D = new int[0][0];

    private Zygote() {}

    /** Called for some security initialization before any fork. */
@@ -231,6 +244,147 @@ public final class Zygote {

    private static native boolean nativeRemoveBlastulaTableEntry(int blastulaPID);

    /**
     * uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal
     * operation. It may also specify any gid and setgroups() list it chooses.
     * In factory test mode, it may specify any UID.
     *
     * @param args non-null; zygote spawner arguments
     * @param peer non-null; peer credentials
     * @throws ZygoteSecurityException
     */
    protected static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
            throws ZygoteSecurityException {

        if (peer.getUid() == Process.SYSTEM_UID) {
            /* In normal operation, SYSTEM_UID can only specify a restricted
             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
             */
            boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;

            if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) {
                throw new ZygoteSecurityException(
                    "System UID may not launch process with UID < "
                        + Process.SYSTEM_UID);
            }
        }

        // If not otherwise specified, uid and gid are inherited from peer
        if (!args.mUidSpecified) {
            args.mUid = peer.getUid();
            args.mUidSpecified = true;
        }
        if (!args.mGidSpecified) {
            args.mGid = peer.getGid();
            args.mGidSpecified = true;
        }
    }

    /**
     * Applies debugger system properties to the zygote arguments.
     *
     * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
     * the debugger state is specified via the "--enable-jdwp" flag
     * in the spawn request.
     *
     * @param args non-null; zygote spawner args
     */
    protected static void applyDebuggerSystemProperty(ZygoteArguments args) {
        if (RoSystemProperties.DEBUGGABLE) {
            args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
        }
    }

    /**
     * Applies zygote security policy.
     * Based on the credentials of the process issuing a zygote command:
     * <ol>
     * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
     * wrapper command.
     * <li> Any other uid may not specify any invoke-with argument.
     * </ul>
     *
     * @param args non-null; zygote spawner arguments
     * @param peer non-null; peer credentials
     * @throws ZygoteSecurityException
     */
    protected static void applyInvokeWithSecurityPolicy(ZygoteArguments args, Credentials peer)
            throws ZygoteSecurityException {
        int peerUid = peer.getUid();

        if (args.mInvokeWith != null && peerUid != 0
                && (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
            throw new ZygoteSecurityException("Peer is permitted to specify an"
                + "explicit invoke-with wrapper command only for debuggable"
                + "applications.");
        }
    }

    /**
     * Applies invoke-with system properties to the zygote arguments.
     *
     * @param args non-null; zygote args
     */
    protected static void applyInvokeWithSystemProperty(ZygoteArguments args) {
        if (args.mInvokeWith == null && args.mNiceName != null) {
            String property = "wrap." + args.mNiceName;
            args.mInvokeWith = SystemProperties.get(property);
            if (args.mInvokeWith != null && args.mInvokeWith.length() == 0) {
                args.mInvokeWith = null;
            }
        }
    }

    /**
     * Reads an argument list from the provided socket
     * @return Argument list or null if EOF is reached
     * @throws IOException passed straight through
     */
    static String[] readArgumentList(BufferedReader socketReader) throws IOException {

        /**
         * See android.os.Process.zygoteSendArgsAndGetPid()
         * 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 argc;

        try {
            String argc_string = socketReader.readLine();

            if (argc_string == null) {
                // EOF reached.
                return null;
            }
            argc = Integer.parseInt(argc_string);

        } catch (NumberFormatException ex) {
            Log.e("Zygote", "Invalid Zygote wire format: non-int at argc");
            throw new IOException("Invalid wire format");
        }

        // See bug 1092107: large argc can be used for a DOS attack
        if (argc > MAX_ZYGOTE_ARGC) {
            throw new IOException("Max arg count exceeded");
        }

        String[] args = new String[argc];
        for (int arg_index = 0; arg_index < argc; arg_index++) {
            args[arg_index] = socketReader.readLine();
            if (args[arg_index] == null) {
                // We got an unexpected EOF.
                throw new IOException("Truncated request");
            }
        }

        return args;
    }


    private static void callPostForkSystemServerHooks() {
        // SystemServer specific post fork hooks run before child post fork hooks.
+390 −0
Original line number Diff line number Diff line
/*
 * 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> --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;

    /**
     * from all --rlimit=r,c,m
     */
    ArrayList<int[]> mRLimits;

    /**
     * from --invoke-with
     */
    String mInvokeWith;

    /**
     * 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;

    /**
     * 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;

    /**
     * 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 {
        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("--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-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 {
                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 (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);
            }
        }
    }
}
+48 −549

File changed.

Preview size limit exceeded, changes collapsed.

+21 −22
Original line number Diff line number Diff line
@@ -73,8 +73,7 @@ import java.security.Security;
 * Pre-initializes some classes, and then waits for commands on a UNIX domain socket. Based on these
 * commands, forks off child processes that inherit the initial state of the VM.
 *
 * Please see {@link ZygoteConnection.Arguments} for documentation on the
 * client protocol.
 * Please see {@link ZygoteArguments} for documentation on the client protocol.
 *
 * @hide
 */
@@ -434,12 +433,12 @@ public class ZygoteInit {
    /**
     * Finish remaining work for the newly forked system server process.
     */
    private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        // set umask to 0077 so new files and directories will default to owner-only permissions.
        Os.umask(S_IRWXG | S_IRWXO);

        if (parsedArgs.niceName != null) {
            Process.setArgV0(parsedArgs.niceName);
        if (parsedArgs.mNiceName != null) {
            Process.setArgV0(parsedArgs.mNiceName);
        }

        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
@@ -458,8 +457,8 @@ public class ZygoteInit {
            }
        }

        if (parsedArgs.invokeWith != null) {
            String[] args = parsedArgs.remainingArgs;
        if (parsedArgs.mInvokeWith != null) {
            String[] args = parsedArgs.mRemainingArgs;
            // If we have a non-null system server class path, we'll have to duplicate the
            // existing arguments and append the classpath to it. ART will handle the classpath
            // correctly when we exec a new process.
@@ -471,15 +470,15 @@ public class ZygoteInit {
                args = amendedArgs;
            }

            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
            WrapperInit.execApplication(parsedArgs.mInvokeWith,
                    parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(), null, args);

            throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
        } else {
            ClassLoader cl = null;
            if (systemServerClasspath != null) {
                cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
                cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);

                Thread.currentThread().setContextClassLoader(cl);
            }
@@ -487,8 +486,8 @@ public class ZygoteInit {
            /*
             * Pass the remaining arguments to SystemServer.
             */
            return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs, cl);
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mRemainingArgs, cl);
        }

        /* should never reach here */
@@ -683,29 +682,29 @@ public class ZygoteInit {
                "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer",
        };
        ZygoteConnection.Arguments parsedArgs = null;
        ZygoteArguments parsedArgs = null;

        int pid;

        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
            parsedArgs = new ZygoteArguments(args);
            Zygote.applyDebuggerSystemProperty(parsedArgs);
            Zygote.applyInvokeWithSystemProperty(parsedArgs);

            boolean profileSystemServer = SystemProperties.getBoolean(
                    "dalvik.vm.profilesystemserver", false);
            if (profileSystemServer) {
                parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
                parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
            }

            /* Request to fork the system server process */
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.runtimeFlags,
                    parsedArgs.mUid, parsedArgs.mGid,
                    parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
                    parsedArgs.mPermittedCapabilities,
                    parsedArgs.mEffectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
+5 −6

File changed.

Preview size limit exceeded, changes collapsed.