diff options
| author | 2016-12-16 23:57:16 +0000 | |
|---|---|---|
| committer | 2016-12-16 23:57:18 +0000 | |
| commit | 2f042c923c20f8d065521bdb34b932ef9fc925c6 (patch) | |
| tree | 5830259eda940bf8c215e7d2122e8c1ca9f879c7 | |
| parent | 0f0ca8e136bc158d8d9fa57ec62f5b5831481fd0 (diff) | |
| parent | 061ee3088a79ab0e07d37d1c0897d51422f29c4e (diff) | |
Merge "Dynamically add the webview_zygote's preloaded APK to the zygote FD whitelist."
| -rw-r--r-- | core/java/com/android/internal/os/WebViewZygoteInit.java | 3 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/Zygote.java | 5 | ||||
| -rw-r--r-- | core/jni/com_android_internal_os_Zygote.cpp | 12 | ||||
| -rw-r--r-- | core/jni/fd_utils.cpp | 171 | ||||
| -rw-r--r-- | core/jni/fd_utils.h | 53 |
5 files changed, 155 insertions, 89 deletions
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index d968e3c939ab..a8a55499f5f3 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -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. diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index fc0ccb75de95..293de3d71332 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -153,6 +153,11 @@ public final class Zygote { 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. */ diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 7eb1150c2a32..cc7b95894a1f 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -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 } }; diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index ecd37a848636..969d336f3cad 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -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", @@ -55,6 +45,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; // This should never happen; the zygote should always have the right set @@ -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) { diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h index 66fd7183b33a..9e3afd910914 100644 --- a/core/jni/fd_utils.h +++ b/core/jni/fd_utils.h @@ -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 |