VolumeManager: limit the scope of remountUid post fork.

We want to be sure we're not allocating memory, holding locks
or otherwise preventing the child process from making progress.

This is a temporary fix of limited scope. In the medium term, it
would be preferable to exec a binary that performs this work for us
as soon as we fork.

Test: manual
Bug: 141678467

Change-Id: I57dbd9b3c887aa27e2dd609abf0ad43c66f4ef2a
This commit is contained in:
Narayan Kamath 2019-11-27 10:53:51 +00:00
parent fe83792eec
commit 02efdf55d2
2 changed files with 67 additions and 37 deletions

View file

@ -28,6 +28,7 @@ cc_defaults {
name: "vold_default_libs", name: "vold_default_libs",
static_libs: [ static_libs: [
"libasync_safe",
"libavb", "libavb",
"libbootloader_message", "libbootloader_message",
"libdm", "libdm",

View file

@ -40,6 +40,7 @@
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <async_safe/log.h>
#include <cutils/fs.h> #include <cutils/fs.h>
#include <utils/Trace.h> #include <utils/Trace.h>
@ -516,6 +517,51 @@ int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>&
return 0; return 0;
} }
// This code is executed after a fork so it's very important that the set of
// methods we call here is strictly limited.
//
// TODO: Get rid of this guesswork altogether and instead exec a process
// immediately after fork to do our bindding for us.
static bool childProcess(const char* storageSource, const char* userSource, int nsFd,
struct dirent* de) {
if (setns(nsFd, CLONE_NEWNS) != 0) {
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns for %s :%s", de->d_name,
strerror(errno));
return false;
}
// NOTE: Inlined from vold::UnmountTree here to avoid using PLOG methods and
// to also protect against future changes that may cause issues across a
// fork.
if (TEMP_FAILURE_RETRY(umount2("/storage/", MNT_DETACH)) < 0 && errno != EINVAL &&
errno != ENOENT) {
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to unmount /storage/ :%s",
strerror(errno));
return false;
}
if (TEMP_FAILURE_RETRY(mount(storageSource, "/storage", NULL, MS_BIND | MS_REC, NULL)) == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s",
storageSource, de->d_name, strerror(errno));
return false;
}
if (TEMP_FAILURE_RETRY(mount(NULL, "/storage", NULL, MS_REC | MS_SLAVE, NULL)) == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "vold",
"Failed to set MS_SLAVE to /storage for %s :%s", de->d_name,
strerror(errno));
return false;
}
if (TEMP_FAILURE_RETRY(mount(userSource, "/storage/self", NULL, MS_BIND, NULL)) == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s",
userSource, de->d_name, strerror(errno));
return false;
}
return true;
}
int VolumeManager::remountUid(uid_t uid, int32_t mountMode) { int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
if (GetBoolProperty(android::vold::kPropFuseSnapshot, false)) { if (GetBoolProperty(android::vold::kPropFuseSnapshot, false)) {
// TODO(135341433): Implement fuse specific logic. // TODO(135341433): Implement fuse specific logic.
@ -557,6 +603,8 @@ int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
int nsFd; int nsFd;
struct stat sb; struct stat sb;
pid_t child; pid_t child;
std::string userSource;
std::string storageSource;
static bool apexUpdatable = android::sysprop::ApexProperties::updatable().value_or(false); static bool apexUpdatable = android::sysprop::ApexProperties::updatable().value_or(false);
@ -632,15 +680,6 @@ int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
goto next; goto next;
} }
if (!(child = fork())) {
if (setns(nsFd, CLONE_NEWNS) != 0) {
PLOG(ERROR) << "Failed to setns for " << de->d_name;
_exit(1);
}
android::vold::UnmountTree("/storage/");
std::string storageSource;
if (mode == "default") { if (mode == "default") {
storageSource = "/mnt/runtime/default"; storageSource = "/mnt/runtime/default";
} else if (mode == "read") { } else if (mode == "read") {
@ -650,29 +689,19 @@ int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
} else if (mode == "full") { } else if (mode == "full") {
storageSource = "/mnt/runtime/full"; storageSource = "/mnt/runtime/full";
} else { } else {
// Sane default of no storage visible // Sane default of no storage visible. No need to fork a child
_exit(0); // to remount uid.
} goto next;
if (TEMP_FAILURE_RETRY(
mount(storageSource.c_str(), "/storage", NULL, MS_BIND | MS_REC, NULL)) == -1) {
PLOG(ERROR) << "Failed to mount " << storageSource << " for " << de->d_name;
_exit(1);
}
if (TEMP_FAILURE_RETRY(mount(NULL, "/storage", NULL, MS_REC | MS_SLAVE, NULL)) == -1) {
PLOG(ERROR) << "Failed to set MS_SLAVE to /storage for " << de->d_name;
_exit(1);
} }
// Mount user-specific symlink helper into place // Mount user-specific symlink helper into place
userid_t user_id = multiuser_get_user_id(uid); userSource = StringPrintf("/mnt/user/%d", multiuser_get_user_id(uid));
std::string userSource(StringPrintf("/mnt/user/%d", user_id)); if (!(child = fork())) {
if (TEMP_FAILURE_RETRY( if (childProcess(storageSource.c_str(), userSource.c_str(), nsFd, de)) {
mount(userSource.c_str(), "/storage/self", NULL, MS_BIND, NULL)) == -1) { _exit(0);
PLOG(ERROR) << "Failed to mount " << userSource << " for " << de->d_name; } else {
_exit(1); _exit(1);
} }
_exit(0);
} }
if (child == -1) { if (child == -1) {