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

Commit dbbee6ef authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Move WebView relro related functionality to its own file."

parents 00d4d01c ed98a156
Loading
Loading
Loading
Loading
+7 −256
Original line number Original line Diff line number Diff line
@@ -18,7 +18,6 @@ package android.webkit;


import android.annotation.SystemApi;
import android.annotation.SystemApi;
import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppGlobals;
import android.app.Application;
import android.app.Application;
import android.content.Context;
import android.content.Context;
@@ -27,27 +26,15 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.Trace;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Log;
import android.util.Log;


import com.android.server.LocalServices;

import dalvik.system.VMRuntime;

import java.lang.reflect.Method;
import java.lang.reflect.Method;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;


/**
/**
 * Top level factory, used creating all the main WebView implementation classes.
 * Top level factory, used creating all the main WebView implementation classes.
@@ -67,14 +54,8 @@ public final class WebViewFactory {
    private static final String NULL_WEBVIEW_FACTORY =
    private static final String NULL_WEBVIEW_FACTORY =
            "com.android.webview.nullwebview.NullWebViewFactoryProvider";
            "com.android.webview.nullwebview.NullWebViewFactoryProvider";


    private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
            "/data/misc/shared_relro/libwebviewchromium32.relro";
    private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
            "/data/misc/shared_relro/libwebviewchromium64.relro";

    public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
    public static final String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY =
            "persist.sys.webview.vmsize";
            "persist.sys.webview.vmsize";
    private static final long CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES = 100 * 1024 * 1024;


    private static final String LOGTAG = "WebViewFactory";
    private static final String LOGTAG = "WebViewFactory";


@@ -84,7 +65,6 @@ public final class WebViewFactory {
    // same provider.
    // same provider.
    private static WebViewFactoryProvider sProviderInstance;
    private static WebViewFactoryProvider sProviderInstance;
    private static final Object sProviderLock = new Object();
    private static final Object sProviderLock = new Object();
    private static boolean sAddressSpaceReserved = false;
    private static PackageInfo sPackageInfo;
    private static PackageInfo sPackageInfo;


    // Error codes for loadWebViewNativeLibraryFromPackage
    // Error codes for loadWebViewNativeLibraryFromPackage
@@ -120,7 +100,7 @@ public final class WebViewFactory {
        return "Unknown";
        return "Unknown";
    }
    }


    private static class MissingWebViewPackageException extends Exception {
    static class MissingWebViewPackageException extends Exception {
        public MissingWebViewPackageException(String message) { super(message); }
        public MissingWebViewPackageException(String message) { super(message); }
        public MissingWebViewPackageException(Exception e) { super(e); }
        public MissingWebViewPackageException(Exception e) { super(e); }
    }
    }
@@ -183,7 +163,7 @@ public final class WebViewFactory {
        }
        }


        try {
        try {
            int loadNativeRet = loadNativeLibrary(clazzLoader, packageInfo);
            int loadNativeRet = WebViewLibraryLoader.loadNativeLibrary(clazzLoader, packageInfo);
            // If we failed waiting for relro we want to return that fact even if we successfully
            // If we failed waiting for relro we want to return that fact even if we successfully
            // load the relro file.
            // load the relro file.
            if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
            if (loadNativeRet == LIBLOAD_SUCCESS) return response.status;
@@ -414,7 +394,7 @@ public final class WebViewFactory {
                ClassLoader clazzLoader = webViewContext.getClassLoader();
                ClassLoader clazzLoader = webViewContext.getClassLoader();


                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
                loadNativeLibrary(clazzLoader, sPackageInfo);
                WebViewLibraryLoader.loadNativeLibrary(clazzLoader, sPackageInfo);
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);


                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
@@ -450,20 +430,7 @@ public final class WebViewFactory {
     */
     */
    public static void prepareWebViewInZygote() {
    public static void prepareWebViewInZygote() {
        try {
        try {
            System.loadLibrary("webviewchromium_loader");
            WebViewLibraryLoader.reserveAddressSpaceInZygote();
            long addressSpaceToReserve =
                    SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
                    CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
            sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);

            if (sAddressSpaceReserved) {
                if (DEBUG) {
                    Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes");
                }
            } else {
                Log.e(LOGTAG, "reserving " + addressSpaceToReserve +
                        " bytes of address space failed");
            }
        } catch (Throwable t) {
        } catch (Throwable t) {
            // Log and discard errors at this stage as we must not crash the zygote.
            // Log and discard errors at this stage as we must not crash the zygote.
            Log.e(LOGTAG, "error preparing native loader", t);
            Log.e(LOGTAG, "error preparing native loader", t);
@@ -479,13 +446,13 @@ public final class WebViewFactory {
        // waiting on relro creation.
        // waiting on relro creation.
        if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
        if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
            if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
            if (DEBUG) Log.v(LOGTAG, "Create 32 bit relro");
            createRelroFile(false /* is64Bit */, nativeLibraryPaths);
            WebViewLibraryLoader.createRelroFile(false /* is64Bit */, nativeLibraryPaths);
            numRelros++;
            numRelros++;
        }
        }


        if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
        if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
            if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
            if (DEBUG) Log.v(LOGTAG, "Create 64 bit relro");
            createRelroFile(true /* is64Bit */, nativeLibraryPaths);
            WebViewLibraryLoader.createRelroFile(true /* is64Bit */, nativeLibraryPaths);
            numRelros++;
            numRelros++;
        }
        }
        return numRelros;
        return numRelros;
@@ -501,49 +468,7 @@ public final class WebViewFactory {
            fixupStubApplicationInfo(packageInfo.applicationInfo,
            fixupStubApplicationInfo(packageInfo.applicationInfo,
                                     AppGlobals.getInitialApplication().getPackageManager());
                                     AppGlobals.getInitialApplication().getPackageManager());


            nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(packageInfo);
            nativeLibs = WebViewLibraryLoader.updateWebViewZygoteVmSize(packageInfo);
            if (nativeLibs != null) {
                long newVmSize = 0L;

                for (String path : nativeLibs) {
                    if (path == null || TextUtils.isEmpty(path)) continue;
                    if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path);
                    File f = new File(path);
                    if (f.exists()) {
                        newVmSize = Math.max(newVmSize, f.length());
                        continue;
                    }
                    if (path.contains("!/")) {
                        String[] split = TextUtils.split(path, "!/");
                        if (split.length == 2) {
                            try (ZipFile z = new ZipFile(split[0])) {
                                ZipEntry e = z.getEntry(split[1]);
                                if (e != null && e.getMethod() == ZipEntry.STORED) {
                                    newVmSize = Math.max(newVmSize, e.getSize());
                                    continue;
                                }
                            }
                            catch (IOException e) {
                                Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e);
                            }
                        }
                    }
                    Log.e(LOGTAG, "error sizing load for " + path);
                }

                if (DEBUG) {
                    Log.v(LOGTAG, "Based on library size, need " + newVmSize +
                            " bytes of address space.");
                }
                // The required memory can be larger than the file on disk (due to .bss), and an
                // upgraded version of the library will likely be larger, so always attempt to
                // reserve twice as much as we think to allow for the library to grow during this
                // boot cycle.
                newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
                Log.d(LOGTAG, "Setting new address space to " + newVmSize);
                SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
                        Long.toString(newVmSize));
            }
        } catch (Throwable t) {
        } catch (Throwable t) {
            // Log and discard errors at this stage as we must not crash the system server.
            // Log and discard errors at this stage as we must not crash the system server.
            Log.e(LOGTAG, "error preparing webview native library", t);
            Log.e(LOGTAG, "error preparing webview native library", t);
@@ -554,173 +479,6 @@ public final class WebViewFactory {
        return prepareWebViewInSystemServer(nativeLibs);
        return prepareWebViewInSystemServer(nativeLibs);
    }
    }


    private static String getLoadFromApkPath(String apkPath,
                                             String[] abiList,
                                             String nativeLibFileName)
            throws MissingWebViewPackageException {
        // Search the APK for a native library conforming to a listed ABI.
        try (ZipFile z = new ZipFile(apkPath)) {
            for (String abi : abiList) {
                final String entry = "lib/" + abi + "/" + nativeLibFileName;
                ZipEntry e = z.getEntry(entry);
                if (e != null && e.getMethod() == ZipEntry.STORED) {
                    // Return a path formatted for dlopen() load from APK.
                    return apkPath + "!/" + entry;
                }
            }
        } catch (IOException e) {
            throw new MissingWebViewPackageException(e);
        }
        return "";
    }

    private static String[] getWebViewNativeLibraryPaths(PackageInfo packageInfo)
            throws MissingWebViewPackageException {
        ApplicationInfo ai = packageInfo.applicationInfo;
        final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai);

        String path32;
        String path64;
        boolean primaryArchIs64bit = VMRuntime.is64BitAbi(ai.primaryCpuAbi);
        if (!TextUtils.isEmpty(ai.secondaryCpuAbi)) {
            // Multi-arch case.
            if (primaryArchIs64bit) {
                // Primary arch: 64-bit, secondary: 32-bit.
                path64 = ai.nativeLibraryDir;
                path32 = ai.secondaryNativeLibraryDir;
            } else {
                // Primary arch: 32-bit, secondary: 64-bit.
                path64 = ai.secondaryNativeLibraryDir;
                path32 = ai.nativeLibraryDir;
            }
        } else if (primaryArchIs64bit) {
            // Single-arch 64-bit.
            path64 = ai.nativeLibraryDir;
            path32 = "";
        } else {
            // Single-arch 32-bit.
            path32 = ai.nativeLibraryDir;
            path64 = "";
        }

        // Form the full paths to the extracted native libraries.
        // If libraries were not extracted, try load from APK paths instead.
        if (!TextUtils.isEmpty(path32)) {
            path32 += "/" + NATIVE_LIB_FILE_NAME;
            File f = new File(path32);
            if (!f.exists()) {
                path32 = getLoadFromApkPath(ai.sourceDir,
                                            Build.SUPPORTED_32_BIT_ABIS,
                                            NATIVE_LIB_FILE_NAME);
            }
        }
        if (!TextUtils.isEmpty(path64)) {
            path64 += "/" + NATIVE_LIB_FILE_NAME;
            File f = new File(path64);
            if (!f.exists()) {
                path64 = getLoadFromApkPath(ai.sourceDir,
                                            Build.SUPPORTED_64_BIT_ABIS,
                                            NATIVE_LIB_FILE_NAME);
            }
        }

        if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64);
        return new String[] { path32, path64 };
    }

    private static void createRelroFile(final boolean is64Bit, String[] nativeLibraryPaths) {
        final String abi =
                is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];

        // crashHandler is invoked by the ActivityManagerService when the isolated process crashes.
        Runnable crashHandler = new Runnable() {
            @Override
            public void run() {
                try {
                    Log.e(LOGTAG, "relro file creator for " + abi + " crashed. Proceeding without");
                    getUpdateService().notifyRelroCreationCompleted();
                } catch (RemoteException e) {
                    Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage());
                }
            }
        };

        try {
            if (nativeLibraryPaths == null
                    || nativeLibraryPaths[0] == null || nativeLibraryPaths[1] == null) {
                throw new IllegalArgumentException(
                        "Native library paths to the WebView RelRo process must not be null!");
            }
            int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
                    RelroFileCreator.class.getName(), nativeLibraryPaths, "WebViewLoader-" + abi, abi,
                    Process.SHARED_RELRO_UID, crashHandler);
            if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
        } catch (Throwable t) {
            // Log and discard errors as we must not crash the system server.
            Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t);
            crashHandler.run();
        }
    }

    private static class RelroFileCreator {
        // Called in an unprivileged child process to create the relro file.
        public static void main(String[] args) {
            boolean result = false;
            boolean is64Bit = VMRuntime.getRuntime().is64Bit();
            try{
                if (args.length != 2 || args[0] == null || args[1] == null) {
                    Log.e(LOGTAG, "Invalid RelroFileCreator args: " + Arrays.toString(args));
                    return;
                }
                Log.v(LOGTAG, "RelroFileCreator (64bit = " + is64Bit + "), " +
                        " 32-bit lib: " + args[0] + ", 64-bit lib: " + args[1]);
                if (!sAddressSpaceReserved) {
                    Log.e(LOGTAG, "can't create relro file; address space not reserved");
                    return;
                }
                result = nativeCreateRelroFile(args[0] /* path32 */,
                                               args[1] /* path64 */,
                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
                                               CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
                if (result && DEBUG) Log.v(LOGTAG, "created relro file");
            } finally {
                // We must do our best to always notify the update service, even if something fails.
                try {
                    getUpdateService().notifyRelroCreationCompleted();
                } catch (RemoteException e) {
                    Log.e(LOGTAG, "error notifying update service", e);
                }

                if (!result) Log.e(LOGTAG, "failed to create relro file");

                // Must explicitly exit or else this process will just sit around after we return.
                System.exit(0);
            }
        }
    }

    // Assumes that we have waited for relro creation
    private static int loadNativeLibrary(ClassLoader clazzLoader, PackageInfo packageInfo)
            throws MissingWebViewPackageException {
        if (!sAddressSpaceReserved) {
            Log.e(LOGTAG, "can't load with relro file; address space not reserved");
            return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
        }

        String[] args = getWebViewNativeLibraryPaths(packageInfo);
        int result = nativeLoadWithRelroFile(args[0] /* path32 */,
                                             args[1] /* path64 */,
                                             CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
                                             CHROMIUM_WEBVIEW_NATIVE_RELRO_64,
                                             clazzLoader);
        if (result != LIBLOAD_SUCCESS) {
            Log.w(LOGTAG, "failed to load with relro file, proceeding without");
        } else if (DEBUG) {
            Log.v(LOGTAG, "loaded with relro file");
        }
        return result;
    }

    private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";
    private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";


    /** @hide */
    /** @hide */
@@ -728,11 +486,4 @@ public final class WebViewFactory {
        return IWebViewUpdateService.Stub.asInterface(
        return IWebViewUpdateService.Stub.asInterface(
                ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
                ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
    }
    }

    private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
    private static native boolean nativeCreateRelroFile(String lib32, String lib64,
                                                        String relro32, String relro64);
    private static native int nativeLoadWithRelroFile(String lib32, String lib64,
                                                      String relro32, String relro64,
                                                      ClassLoader clazzLoader);
}
}
+323 −0

File added.

Preview size limit exceeded, changes collapsed.