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:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user