diff options
author | Mark Salyzyn <salyzyn@google.com> | 2019-05-29 08:49:11 -0700 |
---|---|---|
committer | Mark Salyzyn <salyzyn@google.com> | 2019-07-15 07:37:56 -0700 |
commit | f0dd09c8898a94d58bf4ad950202e6cc21f1604c (patch) | |
tree | b41fde451d971bdd5e9dabd7c331d271a818e287 | |
parent | e818cccd58f3c550ef92c8e0e9ddffbf8916c336 (diff) | |
download | system_core-f0dd09c8898a94d58bf4ad950202e6cc21f1604c.tar.gz system_core-f0dd09c8898a94d58bf4ad950202e6cc21f1604c.tar.bz2 system_core-f0dd09c8898a94d58bf4ad950202e6cc21f1604c.zip |
fs_mgr: overlay: do not allow sub-mounts to be overridden
Prevent sub-mounts from being candidates for overlayfs overrides.
We move them temporarily to hold the references, then move them back
after overlayfs mount.
There is a race condition that can not be solved where the content
of the sub-mount is temporarily unavailable. This operation occurs
in first stage init (no problem) and during the first adb remount or
adb disable-verity that causes the backing storage to be setup.
Workaround will be to time the adb remount/disable-verity so that the
device-specific problems do not surface.
Test: adb-remount-test.sh and manual device configs
Bug: 130131892
Bug: 135647457
Merged-In: I8782d09b7b389d899aed07b3e6c528280af2d6c8
Change-Id: I8782d09b7b389d899aed07b3e6c528280af2d6c8
-rw-r--r-- | fs_mgr/fs_mgr_overlayfs.cpp | 187 |
1 files changed, 184 insertions, 3 deletions
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp index 0a6014d60..f21d529e0 100644 --- a/fs_mgr/fs_mgr_overlayfs.cpp +++ b/fs_mgr/fs_mgr_overlayfs.cpp @@ -32,7 +32,6 @@ #include <unistd.h> #include <algorithm> -#include <map> #include <memory> #include <string> #include <vector> @@ -517,10 +516,166 @@ bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string return ret; } +bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) { + auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE, + nullptr); + if (ret) { + PERROR << "__mount(target=" << mount_point + << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret; + return false; + } + return true; +} + +bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) { + auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr); + if (ret) { + PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret; + return false; + } + return true; +} + +struct mount_info { + std::string mount_point; + bool shared_flag; +}; + +std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) { + std::vector<mount_info> info; + + auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose}; + if (!file) { + PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'"; + return info; + } + + ssize_t len; + size_t alloc_len = 0; + char* line = nullptr; + while ((len = getline(&line, &alloc_len, file.get())) != -1) { + /* if the last character is a newline, shorten the string by 1 byte */ + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } + + static constexpr char delim[] = " \t"; + char* save_ptr; + if (!strtok_r(line, delim, &save_ptr)) { + LERROR << "Error parsing mount ID"; + break; + } + if (!strtok_r(nullptr, delim, &save_ptr)) { + LERROR << "Error parsing parent ID"; + break; + } + if (!strtok_r(nullptr, delim, &save_ptr)) { + LERROR << "Error parsing mount source"; + break; + } + if (!strtok_r(nullptr, delim, &save_ptr)) { + LERROR << "Error parsing root"; + break; + } + + char* p; + if (!(p = strtok_r(nullptr, delim, &save_ptr))) { + LERROR << "Error parsing mount_point"; + break; + } + mount_info entry = {p, false}; + + if (!strtok_r(nullptr, delim, &save_ptr)) { + LERROR << "Error parsing mount_flags"; + break; + } + + while ((p = strtok_r(nullptr, delim, &save_ptr))) { + if ((p[0] == '-') && (p[1] == '\0')) break; + if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true; + } + if (!p) { + LERROR << "Error parsing fields"; + break; + } + info.emplace_back(std::move(entry)); + } + + free(line); + if (info.empty()) { + LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'"; + } + return info; +} + bool fs_mgr_overlayfs_mount(const std::string& mount_point) { auto options = fs_mgr_get_overlayfs_options(mount_point); if (options.empty()) return false; + auto retval = true; + auto save_errno = errno; + + struct move_entry { + std::string mount_point; + std::string dir; + bool shared_flag; + }; + std::vector<move_entry> move; + auto parent_private = false; + auto parent_made_private = false; + auto dev_private = false; + auto dev_made_private = false; + for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) { + if ((entry.mount_point == mount_point) && !entry.shared_flag) { + parent_private = true; + } + if ((entry.mount_point == "/dev") && !entry.shared_flag) { + dev_private = true; + } + + if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) { + continue; + } + if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) { + return android::base::StartsWith(entry.mount_point, it.mount_point + "/"); + }) != move.end()) { + continue; + } + + // use as the bound directory in /dev. + auto new_context = fs_mgr_get_context(entry.mount_point); + if (!new_context.empty() && setfscreatecon(new_context.c_str())) { + PERROR << "setfscreatecon " << new_context; + } + move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX", + entry.shared_flag}; + const auto target = mkdtemp(new_entry.dir.data()); + if (!target) { + retval = false; + save_errno = errno; + PERROR << "temporary directory for MS_BIND"; + setfscreatecon(nullptr); + continue; + } + setfscreatecon(nullptr); + + if (!parent_private && !parent_made_private) { + parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false); + } + if (new_entry.shared_flag) { + new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false); + } + if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) { + retval = false; + save_errno = errno; + if (new_entry.shared_flag) { + fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true); + } + continue; + } + move.emplace_back(std::move(new_entry)); + } + // hijack __mount() report format to help triage auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay"; const auto opt_list = android::base::Split(options, ","); @@ -535,12 +690,38 @@ bool fs_mgr_overlayfs_mount(const std::string& mount_point) { auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME, options.c_str()); if (ret) { + retval = false; + save_errno = errno; PERROR << report << ret; - return false; } else { LINFO << report << ret; - return true; } + + // Move submounts back. + for (const auto& entry : move) { + if (!dev_private && !dev_made_private) { + dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false); + } + + if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) { + retval = false; + save_errno = errno; + } else if (entry.shared_flag && + !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) { + retval = false; + save_errno = errno; + } + rmdir(entry.dir.c_str()); + } + if (dev_made_private) { + fs_mgr_overlayfs_set_shared_mount("/dev", true); + } + if (parent_made_private) { + fs_mgr_overlayfs_set_shared_mount(mount_point, true); + } + + errno = save_errno; + return retval; } // Mount kScratchMountPoint |