diff --git a/native/src/base/mount.rs b/native/src/base/mount.rs index 2a158dd0c..bc330d555 100644 --- a/native/src/base/mount.rs +++ b/native/src/base/mount.rs @@ -16,7 +16,7 @@ impl Utf8CStr { } } - pub fn remount_with_flags(&self, flags: c_ulong) -> OsResult<()> { + pub fn remount_mount_point_flags(&self, flags: c_ulong) -> OsResult<()> { unsafe { libc::mount( ptr::null(), @@ -29,6 +29,19 @@ impl Utf8CStr { } } + pub fn remount_mount_flags(&self, flags: c_ulong) -> OsResult<()> { + unsafe { + libc::mount( + ptr::null(), + self.as_ptr(), + ptr::null(), + libc::MS_REMOUNT | flags, + ptr::null(), + ) + .check_os_err("remount", Some(self), None) + } + } + pub fn remount_with_data(&self, data: &Utf8CStr) -> OsResult<()> { unsafe { libc::mount( diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index 9c0098c24..873cff72e 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -325,29 +325,6 @@ static void handle_request(pollfd *pfd) { } } -static void switch_cgroup(const char *cgroup, int pid) { - char buf[32]; - ssprintf(buf, sizeof(buf), "%s/cgroup.procs", cgroup); - if (access(buf, F_OK) != 0) - return; - int fd = xopen(buf, O_WRONLY | O_APPEND | O_CLOEXEC); - if (fd == -1) - return; - ssprintf(buf, sizeof(buf), "%d\n", pid); - xwrite(fd, buf, strlen(buf)); - close(fd); -} - -static int setcon(const char *con) { - int fd = open("/proc/self/attr/current", O_WRONLY | O_CLOEXEC); - if (fd < 0) - return fd; - size_t len = strlen(con) + 1; - int rc = write(fd, con, len); - close(fd); - return rc != len; -} - static void daemon_entry() { android_logging(); @@ -369,49 +346,15 @@ static void daemon_entry() { if (fd > STDERR_FILENO) close(fd); - setsid(); - setcon(MAGISK_PROC_CON); - rust::daemon_entry(); SDK_INT = MagiskD::Get().sdk_int(); - // Escape from cgroup - int pid = getpid(); - switch_cgroup("/acct", pid); - switch_cgroup("/dev/cg2_bpf", pid); - switch_cgroup("/sys/fs/cgroup", pid); - if (get_prop("ro.config.per_app_memcg") != "false") { - switch_cgroup("/dev/memcg/apps", pid); - } - // Get self stat xstat("/proc/self/exe", &self_st); - // Samsung workaround #7887 - if (access("/system_ext/app/mediatek-res/mediatek-res.apk", F_OK) == 0) { - set_prop("ro.vendor.mtk_model", "0"); - } - - // Cleanups - const char *tmp = get_magisk_tmp(); - char path[64]; - ssprintf(path, sizeof(path), "%s/" ROOTMNT, tmp); - if (access(path, F_OK) == 0) { - file_readline(true, path, [](string_view line) -> bool { - umount2(line.data(), MNT_DETACH); - return true; - }); - } - if (getenv("REMOUNT_ROOT")) { - xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr); - unsetenv("REMOUNT_ROOT"); - } - ssprintf(path, sizeof(path), "%s/" ROOTOVL, tmp); - rm_rf(path); - fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); sockaddr_un addr = {.sun_family = AF_LOCAL}; - ssprintf(addr.sun_path, sizeof(addr.sun_path), "%s/" MAIN_SOCKET, tmp); + ssprintf(addr.sun_path, sizeof(addr.sun_path), "%s/" MAIN_SOCKET, get_magisk_tmp()); unlink(addr.sun_path); if (xbind(fd, (sockaddr *) &addr, sizeof(addr))) exit(1); diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index d83d78259..02707e823 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -1,18 +1,21 @@ -use crate::consts::{MAGISK_FULL_VER, MAIN_CONFIG, SECURE_DIR}; +use crate::consts::{MAGISK_FULL_VER, MAGISK_PROC_CON, MAIN_CONFIG, ROOTMNT, ROOTOVL, SECURE_DIR}; use crate::db::Sqlite3; use crate::ffi::{ DbEntryKey, ModuleInfo, RequestCode, check_key_combo, disable_modules, exec_common_scripts, exec_module_scripts, get_magisk_tmp, initialize_denylist, setup_magisk_env, }; -use crate::get_prop; use crate::logging::{magisk_logging, setup_logfile, start_log_daemon}; use crate::mount::{clean_mounts, setup_mounts}; use crate::package::ManagerInfo; use crate::selinux::restore_tmpcon; use crate::su::SuInfo; -use base::libc::{O_CLOEXEC, O_RDONLY}; -use base::{AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, cstr, error, info, libc}; -use std::io::BufReader; +use crate::{get_prop, set_prop}; +use base::libc::{O_APPEND, O_CLOEXEC, O_RDONLY, O_WRONLY}; +use base::{ + AtomicArc, BufReadExt, FsPathBuilder, ResultExt, Utf8CStr, Utf8CStrBuf, cstr, error, info, libc, +}; +use std::fmt::Write as FmtWrite; +use std::io::{BufReader, Write}; use std::os::unix::net::UnixStream; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Mutex, OnceLock}; @@ -217,6 +220,14 @@ impl MagiskD { } pub fn daemon_entry() { + unsafe { libc::setsid() }; + + // Make sure the current context is magisk + if let Ok(mut current) = cstr!("/proc/self/attr/current").open(O_WRONLY | O_CLOEXEC) { + let con = cstr!(MAGISK_PROC_CON); + current.write_all(con.as_bytes_with_nul()).log_ok(); + } + start_log_daemon(); magisk_logging(); info!("Magisk {} daemon started", MAGISK_FULL_VER); @@ -226,13 +237,13 @@ pub fn daemon_entry() { || get_prop(cstr!("ro.product.device"), false).contains("vsoc"); // Load config status - let path = cstr::buf::new::<64>() - .join_path(get_magisk_tmp()) + let magisk_tmp = get_magisk_tmp(); + let mut tmp_path = cstr::buf::new::<64>() + .join_path(magisk_tmp) .join_path(MAIN_CONFIG); let mut is_recovery = false; - if let Ok(file) = path.open(O_RDONLY | O_CLOEXEC) { - let mut file = BufReader::new(file); - file.foreach_props(|key, val| { + if let Ok(main_config) = tmp_path.open(O_RDONLY | O_CLOEXEC) { + BufReader::new(main_config).foreach_props(|key, val| { if key == "RECOVERYMODE" { is_recovery = val == "true"; return false; @@ -240,11 +251,11 @@ pub fn daemon_entry() { true }); } + tmp_path.truncate(magisk_tmp.len()); let mut sdk_int = -1; - if let Ok(file) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) { - let mut file = BufReader::new(file); - file.foreach_props(|key, val| { + if let Ok(build_prop) = cstr!("/system/build.prop").open(O_RDONLY | O_CLOEXEC) { + BufReader::new(build_prop).foreach_props(|key, val| { if key == "ro.build.version.sdk" { sdk_int = val.parse::().unwrap_or(-1); return false; @@ -262,6 +273,42 @@ pub fn daemon_entry() { restore_tmpcon().log_ok(); + // Escape from cgroup + let pid = unsafe { libc::getpid() }; + switch_cgroup("/acct", pid); + switch_cgroup("/dev/cg2_bpf", pid); + switch_cgroup("/sys/fs/cgroup", pid); + if get_prop(cstr!("ro.config.per_app_memcg"), false) != "false" { + switch_cgroup("/dev/memcg/apps", pid); + } + + // Samsung workaround #7887 + if cstr!("/system_ext/app/mediatek-res/mediatek-res.apk").exists() { + set_prop(cstr!("ro.vendor.mtk_model"), cstr!("0"), false); + } + + // Cleanup pre-init mounts + tmp_path.append_path(ROOTMNT); + if let Ok(mount_list) = tmp_path.open(O_RDONLY | O_CLOEXEC) { + BufReader::new(mount_list).foreach_lines(|line| { + let item = Utf8CStr::from_string(line); + item.unmount().log_ok(); + true + }) + } + tmp_path.truncate(magisk_tmp.len()); + + // Remount rootfs as read-only if requested + if std::env::var_os("REMOUNT_ROOT").is_some() { + cstr!("/").remount_mount_flags(libc::MS_RDONLY).log_ok(); + unsafe { std::env::remove_var("REMOUNT_ROOT") }; + } + + // Remove all pre-init overlay files to free-up memory + tmp_path.append_path(ROOTOVL); + tmp_path.remove_all().log_ok(); + tmp_path.truncate(magisk_tmp.len()); + let magiskd = MagiskD { sdk_int, is_emulator, @@ -272,6 +319,20 @@ pub fn daemon_entry() { MAGISKD.set(magiskd).ok(); } +fn switch_cgroup(cgroup: &str, pid: i32) { + let mut buf = cstr::buf::new::<64>() + .join_path(cgroup) + .join_path("cgroup.procs"); + if !buf.exists() { + return; + } + if let Ok(mut file) = buf.open(O_WRONLY | O_APPEND | O_CLOEXEC) { + buf.clear(); + buf.write_fmt(format_args!("{}", pid)).ok(); + file.write_all(buf.as_bytes()).log_ok(); + } +} + fn check_data() -> bool { if let Ok(file) = cstr!("/proc/mounts").open(O_RDONLY | O_CLOEXEC) { let mut mnt = false; diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index 810386571..ee962869f 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -137,6 +137,8 @@ pub mod ffi { #[cxx_name = "prop_cb"] type PropCb; unsafe fn get_prop_rs(name: *const c_char, persist: bool) -> String; + #[cxx_name = "set_prop"] + unsafe fn set_prop_rs(name: *const c_char, value: *const c_char, skip_svc: bool) -> i32; unsafe fn prop_cb_exec( cb: Pin<&mut PropCb>, name: *const c_char, @@ -277,3 +279,7 @@ impl SuRequest { pub fn get_prop(name: &Utf8CStr, persist: bool) -> String { unsafe { ffi::get_prop_rs(name.as_ptr(), persist) } } + +pub fn set_prop(name: &Utf8CStr, value: &Utf8CStr, skip_svc: bool) -> bool { + unsafe { ffi::set_prop_rs(name.as_ptr(), value.as_ptr(), skip_svc) == 0 } +} diff --git a/native/src/core/mount.rs b/native/src/core/mount.rs index 6338aa858..db780065e 100644 --- a/native/src/core/mount.rs +++ b/native/src/core/mount.rs @@ -74,7 +74,7 @@ pub fn setup_mounts() { let _: LoggedResult<()> = try { module_mnt.mkdir(0o755)?; cstr!(MODULEROOT).bind_mount_to(&module_mnt)?; - module_mnt.remount_with_flags(libc::MS_RDONLY)?; + module_mnt.remount_mount_point_flags(libc::MS_RDONLY)?; }; } diff --git a/native/src/include/consts.hpp b/native/src/include/consts.hpp index 4dd7952c2..ba836ac94 100644 --- a/native/src/include/consts.hpp +++ b/native/src/include/consts.hpp @@ -20,11 +20,8 @@ #define BBPATH INTLROOT "/busybox" #define ROOTOVL INTLROOT "/rootdir" #define SHELLPTS INTLROOT "/pts" -#define ROOTMNT ROOTOVL "/.mount_list" -#define SELINUXMOCK INTLROOT "/selinux" #define MAIN_CONFIG INTLROOT "/config" #define MAIN_SOCKET DEVICEDIR "/socket" -#define LOG_PIPE DEVICEDIR "/log" constexpr const char *applet_names[] = { "su", "resetprop", nullptr }; @@ -39,7 +36,6 @@ constexpr const char *applet_names[] = { "su", "resetprop", nullptr }; #define MAGISK_FILE_CON "u:object_r:" SEPOL_FILE_TYPE ":s0" // Log pipe that only root and zygote can open #define SEPOL_LOG_TYPE "magisk_log_file" -#define MAGISK_LOG_CON "u:object_r:" SEPOL_LOG_TYPE ":s0" extern int SDK_INT; #define APP_DATA_DIR (SDK_INT >= 24 ? "/data/user_de" : "/data/user") diff --git a/native/src/include/consts.rs b/native/src/include/consts.rs index 42305eb6c..746735e71 100644 --- a/native/src/include/consts.rs +++ b/native/src/include/consts.rs @@ -32,6 +32,7 @@ pub const SELINUXMOCK: &str = concatcp!(INTERNAL_DIR, "/selinux"); // Unconstrained domain the daemon and root processes run in pub const SEPOL_PROC_DOMAIN: &str = "magisk"; +pub const MAGISK_PROC_CON: &str = concatcp!("u:r:", SEPOL_PROC_DOMAIN, ":s0"); // Unconstrained file type that anyone can access pub const SEPOL_FILE_TYPE: &str = "magisk_file"; // Log pipe that only root and zygote can open