Proper mount namespace configuration for bionic am: 6866041ff0 am: 43505e3f22
am: 9829a8c5aa
Change-Id: I624273bf06ad96dfee5def80dc16050c52a7a640
diff --git a/init/Android.bp b/init/Android.bp
index 9f5d17d..67688f2 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -110,6 +110,7 @@
"init.cpp",
"keychords.cpp",
"modalias_handler.cpp",
+ "mount_namespace.cpp",
"parser.cpp",
"persistent_properties.cpp",
"persistent_properties.proto",
@@ -166,6 +167,7 @@
exclude_shared_libs: ["libbinder", "libutils"],
},
},
+ ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
}
// Tests
diff --git a/init/Android.mk b/init/Android.mk
index 69c63e1..59d7f11 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -47,6 +47,7 @@
first_stage_init.cpp \
first_stage_main.cpp \
first_stage_mount.cpp \
+ mount_namespace.cpp \
reboot_utils.cpp \
selinux.cpp \
switch_root.cpp \
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 169edbe..b41b035 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -63,6 +63,7 @@
#include "action_manager.h"
#include "bootchart.h"
#include "init.h"
+#include "mount_namespace.h"
#include "parser.h"
#include "property_service.h"
#include "reboot.h"
@@ -1098,6 +1099,14 @@
}
}
+static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
+ if (SwitchToDefaultMountNamespace()) {
+ return Success();
+ } else {
+ return Error() << "Failed to setup runtime bionic";
+ }
+}
+
// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1145,6 +1154,7 @@
{"rmdir", {1, 1, {true, do_rmdir}}},
{"setprop", {2, 2, {true, do_setprop}}},
{"setrlimit", {3, 3, {false, do_setrlimit}}},
+ {"setup_runtime_bionic", {0, 0, {false, do_setup_runtime_bionic}}},
{"start", {1, 1, {false, do_start}}},
{"stop", {1, 1, {false, do_stop}}},
{"swapon_all", {1, 1, {false, do_swapon_all}}},
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 3a4dc6a..79536e4 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -136,10 +136,6 @@
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
}
-static bool IsRecoveryMode() {
- return access("/system/bin/recovery", F_OK) == 0;
-}
-
static Fstab ReadFirstStageFstab() {
Fstab fstab;
if (!ReadFstabFromDt(&fstab)) {
diff --git a/init/init.cpp b/init/init.cpp
index d360fdd..4f4a15f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -54,6 +54,7 @@
#include "first_stage_mount.h"
#include "import_parser.h"
#include "keychords.h"
+#include "mount_namespace.h"
#include "property_service.h"
#include "reboot.h"
#include "reboot_utils.h"
@@ -666,6 +667,10 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
+ if (!SetupMountNamespaces()) {
+ PLOG(FATAL) << "SetupMountNamespaces failed";
+ }
+
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
new file mode 100644
index 0000000..413fe8f
--- /dev/null
+++ b/init/mount_namespace.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mount_namespace.h"
+
+#include <sys/mount.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+namespace android {
+namespace init {
+namespace {
+
+static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker";
+static constexpr const char* kBootstrapLinkerPath = "/system/bin/bootstrap/linker";
+static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
+
+static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/";
+static constexpr const char* kBootstrapBionicLibsDir = "/system/lib/bootstrap/";
+static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
+
+static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64";
+static constexpr const char* kBootstrapLinkerPath64 = "/system/bin/bootstrap/linker64";
+static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
+
+static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/";
+static constexpr const char* kBootstrapBionicLibsDir64 = "/system/lib64/bootstrap/";
+static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
+
+static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
+
+static bool BindMount(const std::string& source, const std::string& mount_point,
+ bool recursive = false) {
+ unsigned long mountflags = MS_BIND;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point;
+ return false;
+ }
+ return true;
+}
+
+static bool MakeShared(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_SHARED;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to change propagation type to shared";
+ return false;
+ }
+ return true;
+}
+
+static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
+ unsigned long mountflags = MS_PRIVATE;
+ if (recursive) {
+ mountflags |= MS_REC;
+ }
+ if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+ PLOG(ERROR) << "Failed to change propagation type to private";
+ return false;
+ }
+ return true;
+}
+
+static int OpenMountNamespace() {
+ int fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ PLOG(ERROR) << "Cannot open fd for current mount namespace";
+ }
+ return fd;
+}
+
+static std::string GetMountNamespaceId() {
+ std::string ret;
+ if (!android::base::Readlink("/proc/self/ns/mnt", &ret)) {
+ PLOG(ERROR) << "Failed to read namespace ID";
+ return "";
+ }
+ return ret;
+}
+
+static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source,
+ const std::string& linker_mount_point,
+ const std::string& lib_mount_dir) {
+ if (access(linker_source.c_str(), F_OK) != 0) {
+ PLOG(INFO) << linker_source << " does not exist. skipping mounting bionic there.";
+ // This can happen for 64-bit bionic in 32-bit only device.
+ // It is okay to skip mounting the 64-bit bionic.
+ return true;
+ }
+ if (!BindMount(linker_source, linker_mount_point)) {
+ return false;
+ }
+ if (!MakePrivate(linker_mount_point)) {
+ return false;
+ }
+ for (const auto& libname : kBionicLibFileNames) {
+ std::string mount_point = lib_mount_dir + libname;
+ std::string source = lib_dir_source + libname;
+ if (!BindMount(source, mount_point)) {
+ return false;
+ }
+ if (!MakePrivate(mount_point)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool IsBionicUpdatable() {
+ static bool result = android::base::GetBoolProperty("ro.apex.IsBionicUpdatable", false);
+ return result;
+}
+
+static android::base::unique_fd bootstrap_ns_fd;
+static android::base::unique_fd default_ns_fd;
+
+static std::string bootstrap_ns_id;
+static std::string default_ns_id;
+
+} // namespace
+
+bool SetupMountNamespaces() {
+ // Set the propagation type of / as shared so that any mounting event (e.g.
+ // /data) is by default visible to all processes. When private mounting is
+ // needed for /foo/bar, then we will make /foo/bar as a mount point (by
+ // bind-mounting by to itself) and set the propagation type of the mount
+ // point to private.
+ if (!MakeShared("/", true /*recursive*/)) return false;
+
+ // Since different files (bootstrap or runtime APEX) should be mounted to
+ // the same mount point paths (e.g. /bionic/bin/linker, /bionic/lib/libc.so,
+ // etc.) across the two mount namespaces, we create a private mount point at
+ // /bionic so that a mount event for the bootstrap bionic in the mount
+ // namespace for pre-apexd processes is not propagated to the other mount
+ // namespace for post-apexd process, and vice versa.
+ //
+ // Other mount points other than /bionic, however, are all still shared.
+ if (!BindMount("/bionic", "/bionic", true /*recursive*/)) return false;
+ if (!MakePrivate("/bionic")) return false;
+
+ // Bind-mount bootstrap bionic.
+ if (!BindMountBionic(kBootstrapLinkerPath, kBootstrapBionicLibsDir, kLinkerMountPoint,
+ kBionicLibsMountPointDir))
+ return false;
+ if (!BindMountBionic(kBootstrapLinkerPath64, kBootstrapBionicLibsDir64, kLinkerMountPoint64,
+ kBionicLibsMountPointDir64))
+ return false;
+
+ bootstrap_ns_fd.reset(OpenMountNamespace());
+ bootstrap_ns_id = GetMountNamespaceId();
+
+ // When bionic is updatable via the runtime APEX, we create separate mount
+ // namespaces for processes that are started before and after the APEX is
+ // activated by apexd. In the namespace for pre-apexd processes, the bionic
+ // from the /system partition (that we call bootstrap bionic) is
+ // bind-mounted. In the namespace for post-apexd processes, the bionic from
+ // the runtime APEX is bind-mounted.
+ bool success = true;
+ if (IsBionicUpdatable() && !IsRecoveryMode()) {
+ // Creating a new namespace by cloning, saving, and switching back to
+ // the original namespace.
+ if (unshare(CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Cannot create mount namespace";
+ return false;
+ }
+ default_ns_fd.reset(OpenMountNamespace());
+ default_ns_id = GetMountNamespaceId();
+
+ // By this unmount, the bootstrap bionic are not mounted in the default
+ // mount namespace.
+ if (umount2("/bionic", MNT_DETACH) == -1) {
+ PLOG(ERROR) << "Cannot unmount /bionic";
+ // Don't return here. We have to switch back to the bootstrap
+ // namespace.
+ success = false;
+ }
+
+ if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
+ return false;
+ }
+ } else {
+ // Otherwise, default == bootstrap
+ default_ns_fd.reset(OpenMountNamespace());
+ default_ns_id = GetMountNamespaceId();
+ }
+
+ LOG(INFO) << "SetupMountNamespaces done";
+ return success;
+}
+
+bool SwitchToDefaultMountNamespace() {
+ if (IsRecoveryMode()) {
+ // we don't have multiple namespaces in recovery mode
+ return true;
+ }
+ if (default_ns_id != GetMountNamespaceId()) {
+ if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
+ return false;
+ }
+ }
+
+ // Bind-mount bionic from the runtime APEX since it is now available. Note
+ // that in case of IsBionicUpdatable() == false, these mounts are over the
+ // existing existing bind mounts for the bootstrap bionic, which effectively
+ // becomes hidden.
+ if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint,
+ kBionicLibsMountPointDir))
+ return false;
+ if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64,
+ kBionicLibsMountPointDir64))
+ return false;
+
+ LOG(INFO) << "Switched to default mount namespace";
+ return true;
+}
+
+bool SwitchToBootstrapMountNamespaceIfNeeded() {
+ if (IsRecoveryMode()) {
+ // we don't have multiple namespaces in recovery mode
+ return true;
+ }
+ if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
+ IsBionicUpdatable()) {
+ if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
+ PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
new file mode 100644
index 0000000..c41a449
--- /dev/null
+++ b/init/mount_namespace.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+namespace init {
+
+bool SetupMountNamespaces();
+bool SwitchToDefaultMountNamespace();
+bool SwitchToBootstrapMountNamespaceIfNeeded();
+
+} // namespace init
+} // namespace android
diff --git a/init/service.cpp b/init/service.cpp
index 272809f..a6eb7f7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -50,6 +50,7 @@
#include <sys/system_properties.h>
#include "init.h"
+#include "mount_namespace.h"
#include "property_service.h"
#include "selinux.h"
#else
@@ -207,6 +208,11 @@
return execv(c_strings[0], c_strings.data()) == 0;
}
+static bool IsRuntimeApexReady() {
+ struct stat buf;
+ return stat("/apex/com.android.runtime/", &buf) == 0;
+}
+
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
@@ -929,6 +935,14 @@
scon = *result;
}
+ if (!IsRuntimeApexReady() && !pre_apexd_) {
+ // If this service is started before the runtime APEX gets available,
+ // mark it as pre-apexd one. Note that this marking is permanent. So
+ // for example, if the service is re-launched (e.g., due to crash),
+ // it is still recognized as pre-apexd... for consistency.
+ pre_apexd_ = true;
+ }
+
LOG(INFO) << "starting service '" << name_ << "'...";
pid_t pid = -1;
@@ -945,6 +959,15 @@
LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
}
+#if defined(__ANDROID__)
+ if (pre_apexd_) {
+ if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+ LOG(FATAL) << "Service '" << name_ << "' could not enter "
+ << "into the bootstrap mount namespace";
+ }
+ }
+#endif
+
if (namespace_flags_ & CLONE_NEWNS) {
if (auto result = SetUpMountNamespace(); !result) {
LOG(FATAL) << "Service '" << name_
diff --git a/init/service.h b/init/service.h
index 56e75b0..c29723a 100644
--- a/init/service.h
+++ b/init/service.h
@@ -242,6 +242,8 @@
std::vector<std::string> args_;
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
+
+ bool pre_apexd_ = false;
};
class ServiceList {
diff --git a/init/util.cpp b/init/util.cpp
index 80fb03d..29d7a76 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -441,5 +441,9 @@
android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
}
+bool IsRecoveryMode() {
+ return access("/system/bin/recovery", F_OK) == 0;
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 2b57910..2232a0f 100644
--- a/init/util.h
+++ b/init/util.h
@@ -64,7 +64,7 @@
bool IsLegalPropertyName(const std::string& name);
void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function);
-
+bool IsRecoveryMode();
} // namespace init
} // namespace android
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index bc8568e..a617418 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -73,6 +73,8 @@
namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
+namespace.default.permitted.paths += /bionic/${LIB}
+namespace.default.permitted.paths += /system/${LIB}/bootstrap
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
@@ -104,6 +106,8 @@
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
+namespace.default.asan.permitted.paths += /bionic/${LIB}
+namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
# If a shared library or an executable requests a shared library that
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0ec2c7d..901b873 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -12,6 +12,12 @@
import /init.${ro.zygote}.rc
on early-init
+ # Mount shared so changes propagate into child namespaces
+ # Do this before other processes are started from init. Otherwise,
+ # processes launched while the propagation type of / is 'private'
+ # won't get mount events from others.
+ mount rootfs rootfs / shared rec
+
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
@@ -346,8 +352,6 @@
# Once everything is setup, no need to modify /.
# The bind+remount combination allows this to work in containers.
mount rootfs rootfs / remount bind ro nodev
- # Mount shared so changes propagate into child namespaces
- mount rootfs rootfs / shared rec
# Mount default storage into root namespace
mount none /mnt/runtime/default /storage bind rec
mount none none /storage slave rec
@@ -583,6 +587,14 @@
# Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
+ # Wait for apexd to finish activating APEXes before starting more processes.
+ # This certainly reduces the parallelism but is required to make as many processes
+ # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
+ # so the impact on the booting time is not significant.
+ wait_for_prop apexd.status ready
+ setup_runtime_bionic
+ parse_apex_configs
+
# If there is no post-fs-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won't work.
@@ -810,6 +822,3 @@
service flash_recovery /system/bin/install-recovery.sh
class main
oneshot
-
-on property:apexd.status=ready
- parse_apex_configs