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