/* * Copyright (C) 2018 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 android.os.Process; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Log; /** * ChildZygoteInit is shared by both the Application and WebView zygote to initialize * and run a (child) Zygote server. * * @hide */ public class ChildZygoteInit { private static final String TAG = "ChildZygoteInit"; static String parseSocketNameFromArgs(String[] argv) { for (String arg : argv) { if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) { return arg.substring(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG.length()); } } return null; } static String parseAbiListFromArgs(String[] argv) { for (String arg : argv) { if (arg.startsWith(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG)) { return arg.substring(Zygote.CHILD_ZYGOTE_ABI_LIST_ARG.length()); } } return null; } static int parseIntFromArg(String[] argv, String desiredArg) { int value = -1; for (String arg : argv) { if (arg.startsWith(desiredArg)) { String valueStr = arg.substring(arg.indexOf('=') + 1); try { value = Integer.parseInt(valueStr); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid int argument: " + valueStr, e); } } } return value; } /** * Starts a ZygoteServer and listens for requests * * @param server An instance of a ZygoteServer to listen on * @param args Passed in arguments for this ZygoteServer */ static void runZygoteServer(ZygoteServer server, String[] args) { String socketName = parseSocketNameFromArgs(args); if (socketName == null) { throw new NullPointerException("No socketName specified"); } String abiList = parseAbiListFromArgs(args); if (abiList == null) { throw new NullPointerException("No abiList specified"); } try { Os.prctl(OsConstants.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); } catch (ErrnoException ex) { throw new RuntimeException("Failed to set PR_SET_NO_NEW_PRIVS", ex); } int uidGidMin = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_START); int uidGidMax = parseIntFromArg(args, Zygote.CHILD_ZYGOTE_UID_RANGE_END); if (uidGidMin == -1 || uidGidMax == -1) { throw new RuntimeException("Couldn't parse UID range start/end"); } if (uidGidMin > uidGidMax) { throw new RuntimeException("Passed in UID range is invalid, min > max."); } // Verify the UIDs at least do not include system UIDs; we can't easily verify there // are just isolated UIDs in the range, because for the webview zygote, there is no // single range that captures all possible isolated UIDs. // TODO(b/123615476) narrow this down if (uidGidMin < Process.FIRST_APP_ZYGOTE_ISOLATED_UID) { throw new RuntimeException("Passed in UID range does not map to isolated processes."); } /** * Install a seccomp filter that ensure this Zygote can only setuid()/setgid() * to the passed in range. */ Zygote.nativeInstallSeccompUidGidFilter(uidGidMin, uidGidMax); final Runnable caller; try { server.registerServerSocketAtAbstractName(socketName); // Add the abstract socket to the FD allow list so that the native zygote code // can properly detach it after forking. Zygote.nativeAllowFileAcrossFork("ABSTRACT/" + socketName); // The select loop returns early in the child process after a fork and // loops forever in the zygote. caller = server.runSelectLoop(abiList); } catch (RuntimeException e) { Log.e(TAG, "Fatal exception:", e); throw e; } finally { server.closeServerSocket(); } // We're in the child process and have exited the select loop. Proceed to execute the // command. if (caller != null) { caller.run(); } } }