140 lines
4.9 KiB
Java
140 lines
4.9 KiB
Java
/*
|
|
* 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();
|
|
}
|
|
}
|
|
}
|