Dynamically add the webview_zygote's preloaded APK to the zygote FD whitelist. am: 54e387ddbe

am: 839a185b2e

Change-Id: I1e761ea737447b347f2542419d18d1f42b1b50db
This commit is contained in:
Robert Sesek
2017-01-10 23:40:15 +00:00
committed by android-build-merger
5 changed files with 155 additions and 89 deletions

View File

@@ -62,6 +62,9 @@ class WebViewZygoteInit {
ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
packagePath, libsPath);
// Add the APK to the Zygote's list of allowed files for children.
Zygote.nativeAllowFileAcrossFork(packagePath);
// Once we have the classloader, look up the WebViewFactoryProvider implementation and
// call preloadInZygote() on it to give it the opportunity to preload the native library
// and perform any other initialisation work that should be shared among the children.

View File

@@ -152,6 +152,11 @@ public final class Zygote {
native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
/**
* Lets children of the zygote inherit open file descriptors to this path.
*/
native protected static void nativeAllowFileAcrossFork(String path);
/**
* Zygote unmount storage space on initializing.
* This method is called once.

View File

@@ -709,6 +709,16 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
return pid;
}
static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
JNIEnv* env, jclass, jstring path) {
ScopedUtfChars path_native(env, path);
const char* path_cstr = path_native.c_str();
if (!path_cstr) {
RuntimeAbort(env, __LINE__, "path_cstr == NULL");
}
FileDescriptorWhitelist::Get()->Allow(path_cstr);
}
static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
// Zygote process unmount root storage space initially before every child processes are forked.
// Every forked child processes (include SystemServer) only mount their own root storage space
@@ -753,6 +763,8 @@ static const JNINativeMethod gMethods[] = {
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
{ "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
(void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
{ "nativeUnmountStorageOnInit", "()V",
(void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
};

View File

@@ -29,17 +29,7 @@
#include <android-base/strings.h>
#include <cutils/log.h>
// Whitelist of open paths that the zygote is allowed to keep open.
//
// In addition to the paths listed here, all files ending with
// ".jar" under /system/framework" are whitelisted. See
// FileDescriptorInfo::IsWhitelisted for the canonical definition.
//
// If the whitelisted path is associated with a regular file or a
// character device, the file is reopened after a fork with the same
// offset and mode. If the whilelisted path is associated with a
// AF_UNIX socket, the socket will refer to /dev/null after each
// fork, and all operations on it will fail.
// Static whitelist of open paths that the zygote is allowed to keep open.
static const char* kPathWhitelist[] = {
"/dev/null",
"/dev/socket/zygote",
@@ -54,6 +44,93 @@ static const char* kPathWhitelist[] = {
static const char kFdPath[] = "/proc/self/fd";
// static
FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
if (instance_ == nullptr) {
instance_ = new FileDescriptorWhitelist();
}
return instance_;
}
bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
// Check the static whitelist path.
for (const auto& whitelist_path : kPathWhitelist) {
if (path == whitelist_path)
return true;
}
// Check any paths added to the dynamic whitelist.
for (const auto& whitelist_path : whitelist_) {
if (path == whitelist_path)
return true;
}
static const std::string kFrameworksPrefix = "/system/framework/";
static const std::string kJarSuffix = ".jar";
if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
return true;
}
// Whitelist files needed for Runtime Resource Overlay, like these:
// /system/vendor/overlay/framework-res.apk
// /system/vendor/overlay-subdir/pg/framework-res.apk
// /vendor/overlay/framework-res.apk
// /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
// /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
// /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
// See AssetManager.cpp for more details on overlay-subdir.
static const std::string kOverlayDir = "/system/vendor/overlay/";
static const std::string kVendorOverlayDir = "/vendor/overlay";
static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
static const std::string kApkSuffix = ".apk";
if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
|| StartsWith(path, kVendorOverlayDir))
&& EndsWith(path, kApkSuffix)
&& path.find("/../") == std::string::npos) {
return true;
}
static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
static const std::string kOverlayIdmapSuffix = ".apk@idmap";
if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
&& path.find("/../") == std::string::npos) {
return true;
}
// All regular files that are placed under this path are whitelisted automatically.
static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
return true;
}
return false;
}
FileDescriptorWhitelist::FileDescriptorWhitelist()
: whitelist_() {
}
// TODO: Call android::base::StartsWith instead of copying the code here.
// static
bool FileDescriptorWhitelist::StartsWith(const std::string& str,
const std::string& prefix) {
return str.compare(0, prefix.size(), prefix) == 0;
}
// TODO: Call android::base::EndsWith instead of copying the code here.
// static
bool FileDescriptorWhitelist::EndsWith(const std::string& str,
const std::string& suffix) {
if (suffix.size() > str.size()) {
return false;
}
return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
// static
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
struct stat f_stat;
@@ -64,13 +141,15 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
return NULL;
}
const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
if (S_ISSOCK(f_stat.st_mode)) {
std::string socket_name;
if (!GetSocketName(fd, &socket_name)) {
return NULL;
}
if (!IsWhitelisted(socket_name)) {
if (!whitelist->IsAllowed(socket_name)) {
ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
return NULL;
}
@@ -98,7 +177,7 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
return NULL;
}
if (!IsWhitelisted(file_path)) {
if (!whitelist->IsAllowed(file_path)) {
ALOGE("Not whitelisted : %s", file_path.c_str());
return NULL;
}
@@ -218,72 +297,6 @@ FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file
is_sock(false) {
}
// TODO: Call android::base::StartsWith instead of copying the code here.
// static
bool FileDescriptorInfo::StartsWith(const std::string& str, const std::string& prefix) {
return str.compare(0, prefix.size(), prefix) == 0;
}
// TODO: Call android::base::EndsWith instead of copying the code here.
// static
bool FileDescriptorInfo::EndsWith(const std::string& str, const std::string& suffix) {
if (suffix.size() > str.size()) {
return false;
}
return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
// static
bool FileDescriptorInfo::IsWhitelisted(const std::string& path) {
for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
if (kPathWhitelist[i] == path) {
return true;
}
}
static const std::string kFrameworksPrefix = "/system/framework/";
static const std::string kJarSuffix = ".jar";
if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
return true;
}
// Whitelist files needed for Runtime Resource Overlay, like these:
// /system/vendor/overlay/framework-res.apk
// /system/vendor/overlay-subdir/pg/framework-res.apk
// /vendor/overlay/framework-res.apk
// /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
// /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
// /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
// See AssetManager.cpp for more details on overlay-subdir.
static const std::string kOverlayDir = "/system/vendor/overlay/";
static const std::string kVendorOverlayDir = "/vendor/overlay";
static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
static const std::string kApkSuffix = ".apk";
if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
|| StartsWith(path, kVendorOverlayDir))
&& EndsWith(path, kApkSuffix)
&& path.find("/../") == std::string::npos) {
return true;
}
static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
static const std::string kOverlayIdmapSuffix = ".apk@idmap";
if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
&& path.find("/../") == std::string::npos) {
return true;
}
// All regular files that are placed under this path are whitelisted automatically.
static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
return true;
}
return false;
}
// TODO: Call android::base::Readlink instead of copying the code here.
// static
bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {

View File

@@ -20,6 +20,7 @@
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include <dirent.h>
#include <inttypes.h>
@@ -27,6 +28,48 @@
#include <android-base/macros.h>
// Whitelist of open paths that the zygote is allowed to keep open.
//
// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
// paths dynamically added with Allow(), all files ending with ".jar"
// under /system/framework" are whitelisted. See IsAllowed() for the canonical
// definition.
//
// If the whitelisted path is associated with a regular file or a
// character device, the file is reopened after a fork with the same
// offset and mode. If the whilelisted path is associated with a
// AF_UNIX socket, the socket will refer to /dev/null after each
// fork, and all operations on it will fail.
class FileDescriptorWhitelist {
public:
// Lazily creates the global whitelist.
static FileDescriptorWhitelist* Get();
// Adds a path to the whitelist.
void Allow(const std::string& path) {
whitelist_.push_back(path);
}
// Returns true iff. a given path is whitelisted. A path is whitelisted
// if it belongs to the whitelist (see kPathWhitelist) or if it's a path
// under /system/framework that ends with ".jar" or if it is a system
// framework overlay.
bool IsAllowed(const std::string& path) const;
private:
FileDescriptorWhitelist();
static bool StartsWith(const std::string& str, const std::string& prefix);
static bool EndsWith(const std::string& str, const std::string& suffix);
static FileDescriptorWhitelist* instance_;
std::vector<std::string> whitelist_;
DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
};
// Keeps track of all relevant information (flags, offset etc.) of an
// open zygote file descriptor.
class FileDescriptorInfo {
@@ -56,16 +99,6 @@ class FileDescriptorInfo {
FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
int fd_flags, int fs_flags, off_t offset);
static bool StartsWith(const std::string& str, const std::string& prefix);
static bool EndsWith(const std::string& str, const std::string& suffix);
// Returns true iff. a given path is whitelisted. A path is whitelisted
// if it belongs to the whitelist (see kPathWhitelist) or if it's a path
// under /system/framework that ends with ".jar" or if it is a system
// framework overlay.
static bool IsWhitelisted(const std::string& path);
static bool Readlink(const int fd, std::string* result);
// Returns the locally-bound name of the socket |fd|. Returns true