Replace use of overlayfs' override_creds with overlay_remounter
This new solution does not require the use of non-upstreamed
override_creds patches.
Test: system/core/fs_mgr/tests/adb-remount-test.sh
Bug: 388912628
Change-Id: I2bc86a7171c014f88786c6ab97c8788ad024b264
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 88e26cf..40a3a91 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2335,6 +2335,11 @@
if (!fs_mgr_filesystem_available("overlay")) {
return {.supported = false};
}
+
+ if (!use_override_creds) {
+ return {.supported = true};
+ }
+
struct utsname uts;
if (uname(&uts) == -1) {
return {.supported = false};
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index bf68b2c..a232bf1 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -43,5 +43,11 @@
// overlays if any partition is flashed or updated.
void TeardownAllOverlayForMountPoint(const std::string& mount_point = {});
+// Are we using overlayfs's non-upstreamed override_creds feature?
+// b/388912628 removes the need for override_creds
+// Once this bug is fixed and has had enough soak time, remove this variable and hard code to false
+// where it used
+constexpr bool use_override_creds = true;
+
} // namespace fs_mgr
} // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index ed19b4b..dbdf80b 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -136,6 +136,8 @@
"-DWORLD_WRITABLE_KMSG=1",
"-UDUMP_ON_UMOUNT_FAILURE",
"-DDUMP_ON_UMOUNT_FAILURE=1",
+ "-UALLOW_REMOUNT_OVERLAYS",
+ "-DALLOW_REMOUNT_OVERLAYS=1",
],
},
eng: {
@@ -263,7 +265,10 @@
name: "init",
required: [
"init_second_stage",
- ],
+ ] + select(product_variable("debuggable"), {
+ true: ["overlay_remounter"],
+ false: [],
+ }),
}
cc_defaults {
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index e06a645..6bb0ad7 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -402,7 +402,7 @@
// /second_stage_resources is used to preserve files from first to second
// stage init
- CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 6316b4d..8bdf5b6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -69,6 +69,7 @@
#include <android/avf_cc_flags.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
#include <genfslabelsversion.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
@@ -77,6 +78,7 @@
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
#include "reboot_utils.h"
+#include "second_stage_resources.h"
#include "snapuserd_transition.h"
#include "util.h"
@@ -698,6 +700,65 @@
}
}
+#ifdef ALLOW_REMOUNT_OVERLAYS
+void SetupOverlays() {
+ if (android::fs_mgr::use_override_creds) return;
+
+ bool has_overlays = false;
+ std::string contents;
+ auto result = android::base::ReadFileToString("/proc/mounts", &contents, true);
+
+ auto lines = android::base::Split(contents, "\n");
+ for (auto const& line : lines)
+ if (android::base::StartsWith(line, "overlay")) {
+ has_overlays = true;
+ break;
+ }
+
+ if (!has_overlays) return;
+
+ // After adb remount, we mount all r/o volumes with overlayfs to allow writing.
+ // However, since overlayfs performs its file operations in the context of the
+ // mounting process, this will not work as is - init is in the kernel domain in
+ // first stage, which has very limited permissions.
+
+ // In order to fix this, we need to unmount remount all these volumes from a process
+ // with sufficient privileges to be able to perform these operations. The
+ // overlay_remounter domain has those privileges on debuggable devices.
+ // We will call overlay_remounter which will do the unmounts/mounts.
+ // But for that to work, the volumes must not be busy, so we need to copy
+ // overlay_remounter from system to a ramdisk and run it from there.
+
+ const char* kOverlayRemounter = "overlay_remounter";
+ auto or_src = std::filesystem::path("/system/xbin/") / kOverlayRemounter;
+ auto or_dest = std::filesystem::path(kSecondStageRes) / kOverlayRemounter;
+ std::error_code ec;
+ std::filesystem::copy(or_src, or_dest, ec);
+ if (ec) {
+ LOG(FATAL) << "Failed to copy " << or_src << " to " << or_dest << " " << ec.message();
+ }
+
+ if (selinux_android_restorecon(or_dest.c_str(), 0) == -1) {
+ PLOG(FATAL) << "restorecon of " << or_dest << " failed";
+ }
+ auto dest = unique_fd(open(or_dest.c_str(), O_RDONLY | O_CLOEXEC));
+ if (dest.get() == -1) {
+ PLOG(FATAL) << "Failed to reopen " << or_dest;
+ }
+ if (unlink(or_dest.c_str()) == -1) {
+ PLOG(FATAL) << "Failed to unlink " << or_dest;
+ }
+ const char* args[] = {or_dest.c_str(), nullptr};
+ fexecve(dest.get(), const_cast<char**>(args), nullptr);
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never return from this function.
+ PLOG(FATAL) << "execv(\"" << or_dest << "\") failed";
+}
+#else
+void SetupOverlays() {}
+#endif
+
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
@@ -738,6 +799,10 @@
setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
+ // SetupOverlays does not return if overlays exist, instead it execs overlay_remounter
+ // which then execs second stage init
+ SetupOverlays();
+
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
diff --git a/overlay_remounter/Android.bp b/overlay_remounter/Android.bp
new file mode 100644
index 0000000..d74f7da
--- /dev/null
+++ b/overlay_remounter/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2025 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.
+//
+
+cc_binary {
+ name: "overlay_remounter",
+ srcs: [
+ "overlay_remounter.cpp",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libbase",
+ "liblog",
+ ],
+ system_shared_libs: [],
+ static_executable: true,
+ install_in_xbin: true,
+}
diff --git a/overlay_remounter/overlay_remounter.cpp b/overlay_remounter/overlay_remounter.cpp
new file mode 100644
index 0000000..ddf97fa
--- /dev/null
+++ b/overlay_remounter/overlay_remounter.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 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 <sys/mount.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+int main(int /*argc*/, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ LOG(INFO) << "Overlay remounter will remount all overlay mount points in the overlay_remounter "
+ "domain";
+
+ // Remount ouerlayfs
+ std::string contents;
+ auto result = android::base::ReadFileToString("/proc/mounts", &contents, true);
+
+ auto lines = android::base::Split(contents, "\n");
+ for (auto const& line : lines) {
+ if (!android::base::StartsWith(line, "overlay")) {
+ continue;
+ }
+ auto bits = android::base::Split(line, " ");
+ if (int result = umount(bits[1].c_str()); result == -1) {
+ PLOG(FATAL) << "umount FAILED: " << bits[1];
+ }
+ std::string options;
+ for (auto const& option : android::base::Split(bits[3], ",")) {
+ if (option == "ro" || option == "seclabel" || option == "noatime") continue;
+ if (!options.empty()) options += ',';
+ options += option;
+ }
+ result = mount("overlay", bits[1].c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+ options.c_str());
+ if (result == 0) {
+ LOG(INFO) << "mount succeeded: " << bits[1] << " " << options;
+ } else {
+ PLOG(FATAL) << "mount FAILED: " << bits[1] << " " << bits[3];
+ }
+ }
+
+ const char* path = "/system/bin/init";
+ const char* args[] = {path, "second_stage", nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never return from this function.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+ return 1;
+}