Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54e387dd authored by Robert Sesek's avatar Robert Sesek Committed by Andreas Gampe
Browse files

Dynamically add the webview_zygote's preloaded APK to the zygote FD whitelist.

This refactors the whitelist to be a class, rather than just a static C array.
The whitelist can then be augmented dynamically when the package path is known
in the webview_zygote.

Test: m
Test: sailfish boots
Test: Enable Multi-process WebView in developer options, perform a search in GSA.

Bug: 21643067
Change-Id: Ia1f2535c7275b42b309631b4fe7859c30cbf7309
(cherry picked from commit 061ee308)
parent 8225b7c9
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -62,6 +62,9 @@ class WebViewZygoteInit {
            ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
            ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
                    packagePath, libsPath);
                    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
            // 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
            // 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.
            // and perform any other initialisation work that should be shared among the children.
+5 −0
Original line number Original line Diff line number Diff line
@@ -152,6 +152,11 @@ public final class Zygote {
    native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
    native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
            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.
     * Zygote unmount storage space on initializing.
     * This method is called once.
     * This method is called once.
+12 −0
Original line number Original line Diff line number Diff line
@@ -684,6 +684,16 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
  return pid;
  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) {
static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
    // Zygote process unmount root storage space initially before every child processes are forked.
    // 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
    // Every forked child processes (include SystemServer) only mount their own root storage space
@@ -728,6 +738,8 @@ static const JNINativeMethod gMethods[] = {
      (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
      (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
    { "nativeForkSystemServer", "(II[II[[IJJ)I",
    { "nativeForkSystemServer", "(II[II[[IJJ)I",
      (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
      (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
    { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
      (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
    { "nativeUnmountStorageOnInit", "()V",
    { "nativeUnmountStorageOnInit", "()V",
      (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
      (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
};
};
+92 −79
Original line number Original line Diff line number Diff line
@@ -29,17 +29,7 @@
#include <android-base/strings.h>
#include <android-base/strings.h>
#include <cutils/log.h>
#include <cutils/log.h>


// Whitelist of open paths that the zygote is allowed to keep open.
// Static 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 const char* kPathWhitelist[] = {
static const char* kPathWhitelist[] = {
  "/dev/null",
  "/dev/null",
  "/dev/socket/zygote",
  "/dev/socket/zygote",
@@ -54,6 +44,93 @@ static const char* kPathWhitelist[] = {


static const char kFdPath[] = "/proc/self/fd";
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
// static
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
  struct stat f_stat;
  struct stat f_stat;
@@ -64,13 +141,15 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
    return NULL;
    return NULL;
  }
  }


  const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();

  if (S_ISSOCK(f_stat.st_mode)) {
  if (S_ISSOCK(f_stat.st_mode)) {
    std::string socket_name;
    std::string socket_name;
    if (!GetSocketName(fd, &socket_name)) {
    if (!GetSocketName(fd, &socket_name)) {
      return NULL;
      return NULL;
    }
    }


    if (!IsWhitelisted(socket_name)) {
    if (!whitelist->IsAllowed(socket_name)) {
      ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
      ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
      return NULL;
      return NULL;
    }
    }
@@ -98,7 +177,7 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
    return NULL;
    return NULL;
  }
  }


  if (!IsWhitelisted(file_path)) {
  if (!whitelist->IsAllowed(file_path)) {
    ALOGE("Not whitelisted : %s", file_path.c_str());
    ALOGE("Not whitelisted : %s", file_path.c_str());
    return NULL;
    return NULL;
  }
  }
@@ -218,72 +297,6 @@ FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file
  is_sock(false) {
  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.
// TODO: Call android::base::Readlink instead of copying the code here.
// static
// static
bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
+43 −10
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@
#include <set>
#include <set>
#include <string>
#include <string>
#include <unordered_map>
#include <unordered_map>
#include <vector>


#include <dirent.h>
#include <dirent.h>
#include <inttypes.h>
#include <inttypes.h>
@@ -27,6 +28,48 @@


#include <android-base/macros.h>
#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
// Keeps track of all relevant information (flags, offset etc.) of an
// open zygote file descriptor.
// open zygote file descriptor.
class FileDescriptorInfo {
class FileDescriptorInfo {
@@ -56,16 +99,6 @@ class FileDescriptorInfo {
  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
                     int fd_flags, int fs_flags, off_t offset);
                     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);
  static bool Readlink(const int fd, std::string* result);


  // Returns the locally-bound name of the socket |fd|. Returns true
  // Returns the locally-bound name of the socket |fd|. Returns true