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

Commit 3926f081 authored by Christian Wailes's avatar Christian Wailes Committed by android-build-merger
Browse files

Merge "Refactored ZygoteConnection.Arguments to ZygoteArguments."

am: 3a46c1b4

Change-Id: Id702f32a15ad134b6d694d0fc53e1721094f78dd
parents 6b7e35d2 3a46c1b4
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.