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

Commit 76b4b2c3 authored by Andreas Gampe's avatar Andreas Gampe
Browse files

Zygote: Load system server code early

In an effort to allow loading integrity-checked artifacts from
the dalvik-cache, attempt to create and cache the system server's
classloader early, while still being in the system_server_startup
selinux domain.

The advantage of this approach is that allowances for loading
from the cache are restricted to startup.

Bug: 128688902
Test: m
Test: Device boots, picks up /system artifacts
Test: Device boots, picks up integrity-checked /data artifacts
Change-Id: If4a75fa106db09f1bd666d6d8df7ac3ac3e35a8c
parent 289cd11c
Loading
Loading
Loading
Loading
+40 −7
Original line number Diff line number Diff line
@@ -122,6 +122,12 @@ public class ZygoteInit {

    private static boolean sPreloadComplete;

    /**
     * Cached classloader to use for the system server. Will only be populated in the system
     * server process.
     */
    private static ClassLoader sCachedSystemServerClassLoader = null;

    static void preload(TimingsTraceLog bootTimingsTraceLog) {
        Log.d(TAG, "begin preload");
        bootTimingsTraceLog.traceBegin("BeginPreload");
@@ -443,7 +449,13 @@ public class ZygoteInit {

        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
        if (systemServerClasspath != null) {
            performSystemServerDexOpt(systemServerClasspath);
            if (performSystemServerDexOpt(systemServerClasspath)) {
                // Throw away the cached classloader. If we compiled here, the classloader would
                // not have had AoT-ed artifacts.
                // Note: This only works in a very special environment where selinux enforcement is
                // disabled, e.g., Mac builds.
                sCachedSystemServerClassLoader = null;
            }
            // Capturing profiles is only supported for debug or eng builds since selinux normally
            // prevents it.
            boolean profileSystemServer = SystemProperties.getBoolean(
@@ -476,10 +488,9 @@ public class ZygoteInit {

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

            createSystemServerClassLoader();
            ClassLoader cl = sCachedSystemServerClassLoader;
            if (cl != null) {
                Thread.currentThread().setContextClassLoader(cl);
            }

@@ -493,6 +504,24 @@ public class ZygoteInit {
        /* should never reach here */
    }

    /**
     * Create the classloader for the system server and store it in
     * {@link sCachedSystemServerClassLoader}. This function may be called through JNI in
     * system server startup, when the runtime is in a critically low state. Do not do
     * extended computation etc here.
     */
    private static void createSystemServerClassLoader() {
        if (sCachedSystemServerClassLoader != null) {
            return;
        }
        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
        // TODO: Should we run optimization here?
        if (systemServerClasspath != null) {
            sCachedSystemServerClassLoader = createPathClassLoader(systemServerClasspath,
                    VMRuntime.SDK_VERSION_CUR_DEVELOPMENT);
        }
    }

    /**
     * Note that preparing the profiles for system server does not require special selinux
     * permissions. From the installer perspective the system server is a regular package which can
@@ -557,15 +586,16 @@ public class ZygoteInit {

    /**
     * Performs dex-opt on the elements of {@code classPath}, if needed. We choose the instruction
     * set of the current runtime.
     * set of the current runtime. If something was compiled, return true.
     */
    private static void performSystemServerDexOpt(String classPath) {
    private static boolean performSystemServerDexOpt(String classPath) {
        final String[] classPathElements = classPath.split(":");
        final IInstalld installd = IInstalld.Stub
                .asInterface(ServiceManager.getService("installd"));
        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();

        String classPathForElement = "";
        boolean compiledSomething = false;
        for (String classPathElement : classPathElements) {
            // System server is fully AOTed and never profiled
            // for profile guided compilation.
@@ -607,6 +637,7 @@ public class ZygoteInit {
                            uuid, classLoaderContext, seInfo, false /* downgrade */,
                            targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
                            "server-dexopt");
                    compiledSomething = true;
                } catch (RemoteException | ServiceSpecificException e) {
                    // Ignore (but log), we need this on the classpath for fallback mode.
                    Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -617,6 +648,8 @@ public class ZygoteInit {
            classPathForElement = encodeSystemServerClassPath(
                    classPathForElement, classPathElement);
        }

        return compiledSomething;
    }

    /**
+23 −2
Original line number Diff line number Diff line
@@ -111,11 +111,16 @@ static pid_t gSystemServerPid = 0;

static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
static const char kIsolatedStorageSnapshot[] = "sys.isolated_storage_snapshot";
static const char kZygoteClassName[] = "com/android/internal/os/Zygote";

static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
static jmethodID gCallPostForkSystemServerHooks;
static jmethodID gCallPostForkChildHooks;

static constexpr const char* kZygoteInitClassName = "com/android/internal/os/ZygoteInit";
static jclass gZygoteInitClass;
static jmethodID gCreateSystemServerClassLoader;

static bool g_is_security_enforced = true;

/**
@@ -1420,6 +1425,15 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
      fail_fn("Error calling post fork system server hooks.");
    }

    // Prefetch the classloader for the system server. This is done early to
    // allow a tie-down of the proper system server selinux domain.
    env->CallStaticVoidMethod(gZygoteInitClass, gCreateSystemServerClassLoader);
    if (env->ExceptionCheck()) {
      // Be robust here. The Java code will attempt to create the classloader
      // at a later point (but may not have rights to use AoT artifacts).
      env->ExceptionClear();
    }

    // TODO(oth): Remove hardcoded label here (b/117874058).
    static const char* kSystemServerLabel = "u:r:system_server:s0";
    if (selinux_android_setcon(kSystemServerLabel) != 0) {
@@ -1986,6 +2000,13 @@ int register_com_android_internal_os_Zygote(JNIEnv* env) {
  gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
                                                   "(IZZLjava/lang/String;)V");

  return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
  gZygoteInitClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteInitClassName));
  gCreateSystemServerClassLoader = GetStaticMethodIDOrDie(env, gZygoteInitClass,
                                                          "createSystemServerClassLoader",
                                                          "()V");

  RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));

  return JNI_OK;
}
}  // namespace android