diff --git a/.gitmodules b/.gitmodules index 4473536d4dfaf..07558620e00c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,6 +34,9 @@ [submodule "system_properties"] path = native/src/external/system_properties url = https://github.com/topjohnwu/system_properties.git +[submodule "crt0"] + path = native/src/external/crt0 + url = https://github.com/topjohnwu/crt0.git [submodule "termux-elf-cleaner"] path = tools/termux-elf-cleaner url = https://github.com/termux/termux-elf-cleaner.git diff --git a/build.py b/build.py index ccbc9bee276d4..7a29de90e1f6b 100755 --- a/build.py +++ b/build.py @@ -421,6 +421,7 @@ def build_binary(args): if "magiskinit" in args.target: flag += " B_INIT=1" + flag += " B_CRT0=1" if flag: dump_bin_header(args) @@ -555,27 +556,6 @@ def setup_ndk(args): rm_rf(ndk_path) mv(ondk_path, ndk_path) - header("* Patching static libs") - for target in ["arm-linux-androideabi", "i686-linux-android"]: - arch = target.split("-")[0] - lib_dir = op.join( - ndk_path, - "toolchains", - "llvm", - "prebuilt", - f"{os_name}-x86_64", - "sysroot", - "usr", - "lib", - f"{target}", - "23", - ) - if not op.exists(lib_dir): - continue - src_dir = op.join("tools", "ndk-bins", arch) - rm(op.join(src_dir, ".DS_Store")) - shutil.copytree(src_dir, lib_dir, copy_function=cp, dirs_exist_ok=True) - def push_files(args, script): abi = cmd_out([adb_path, "shell", "getprop", "ro.product.cpu.abi"]) diff --git a/native/src/Android.mk b/native/src/Android.mk index 0a6b82942f1f8..889794e735161 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -63,7 +63,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := magiskinit LOCAL_STATIC_LIBRARIES := \ libbase \ - libcompat \ libpolicy \ libxz \ libinit-rs @@ -77,6 +76,13 @@ LOCAL_SRC_FILES := \ init/selinux.cpp \ init/init-rs.cpp +LOCAL_LDFLAGS := -static -T src/lto_fix.lds + +ifdef B_CRT0 +LOCAL_STATIC_LIBRARIES += crt0 +LOCAL_LDFLAGS := +endif + include $(BUILD_EXECUTABLE) endif @@ -87,7 +93,6 @@ include $(CLEAR_VARS) LOCAL_MODULE := magiskboot LOCAL_STATIC_LIBRARIES := \ libbase \ - libcompat \ liblzma \ liblz4 \ libbz2 \ @@ -102,6 +107,8 @@ LOCAL_SRC_FILES := \ boot/format.cpp \ boot/boot-rs.cpp +LOCAL_LDFLAGS := -static -T src/lto_fix.lds + include $(BUILD_EXECUTABLE) endif diff --git a/native/src/Application.mk b/native/src/Application.mk index 3ffe46736fece..69cf63bc3d262 100644 --- a/native/src/Application.mk +++ b/native/src/Application.mk @@ -8,6 +8,15 @@ APP_PLATFORM := android-23 APP_THIN_ARCHIVE := true APP_STRIP_MODE := none +ifdef B_CRT0 + +# Disable all security and debugging features +APP_CFLAGS += -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-stack-protector -U_FORTIFY_SOURCE +# Override output folder to make sure all dependencies are rebuilt with new CFLAGS +NDK_APP_OUT := ./obj/nolibc + +endif + # Busybox should use stock libc.a ifdef B_BB APP_PLATFORM := android-26 diff --git a/native/src/Cargo.lock b/native/src/Cargo.lock index d92e310f4e888..ebf6ba7783028 100644 --- a/native/src/Cargo.lock +++ b/native/src/Cargo.lock @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.14.1" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" dependencies = [ "bytemuck_derive", ] @@ -135,12 +135,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" [[package]] name = "cfg-if" @@ -543,9 +540,9 @@ dependencies = [ [[package]] name = "num-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", @@ -554,19 +551,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -575,9 +571,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -800,18 +796,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -892,9 +888,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" dependencies = [ "proc-macro2", "quote", @@ -921,18 +917,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", diff --git a/native/src/base/Android.mk b/native/src/base/Android.mk index b34312c572af0..8c60f21256d09 100644 --- a/native/src/base/Android.mk +++ b/native/src/base/Android.mk @@ -22,18 +22,3 @@ LOCAL_SRC_FILES := \ base-rs.cpp \ ../external/cxx-rs/src/cxx.cc include $(BUILD_STATIC_LIBRARY) - -# All static executables should link with libcompat - -include $(CLEAR_VARS) -LOCAL_MODULE := libcompat -LOCAL_SRC_FILES := compat/compat.cpp -# Fix static variables' ctor/dtor when using LTO -# See: https://github.com/android/ndk/issues/1461 -LOCAL_EXPORT_LDFLAGS := -static -T src/lto_fix.lds -Wl,--wrap=rename -Wl,--wrap=renameat -# For some reason, using the hacky libc.a with x86 will trigger stack protection violation -# when mixing Rust and C++ code. Disable stack protector to bypass this issue. -ifeq ($(TARGET_ARCH), x86) -LOCAL_EXPORT_CFLAGS := -fno-stack-protector -endif -include $(BUILD_STATIC_LIBRARY) diff --git a/native/src/base/cstr.rs b/native/src/base/cstr.rs index ede5778766fc0..c408c794f3b7c 100644 --- a/native/src/base/cstr.rs +++ b/native/src/base/cstr.rs @@ -2,9 +2,10 @@ use std::cmp::min; use std::ffi::{CStr, FromBytesWithNulError, OsStr}; use std::fmt::{Arguments, Debug, Display, Formatter, Write}; use std::ops::{Deref, DerefMut}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::str::{Utf8Chunks, Utf8Error}; use std::{fmt, mem, slice, str}; +use std::os::unix::ffi::OsStrExt; use cxx::{type_id, ExternType}; use libc::c_char; @@ -105,7 +106,7 @@ trait AsUtf8CStr { // Implementation for Utf8CString -trait StringExt { +pub trait StringExt { fn nul_terminate(&mut self) -> &mut [u8]; } @@ -122,6 +123,21 @@ impl StringExt for String { } } +impl StringExt for PathBuf { + #[allow(mutable_transmutes)] + fn nul_terminate(&mut self) -> &mut [u8] { + self.reserve(1); + // SAFETY: the PathBuf is reserved to have enough capacity to fit in the null byte + // SAFETY: the null byte is explicitly added outside of the PathBuf's length + unsafe { + let bytes: &mut [u8] = mem::transmute(self.as_mut_os_str().as_bytes()); + let buf = slice::from_raw_parts_mut(bytes.as_mut_ptr(), bytes.len() + 1); + *buf.get_unchecked_mut(bytes.len()) = b'\0'; + buf + } + } +} + #[derive(Default)] pub struct Utf8CString(String); diff --git a/native/src/base/files.cpp b/native/src/base/files.cpp index e91404470df4c..d3a85aa08d053 100644 --- a/native/src/base/files.cpp +++ b/native/src/base/files.cpp @@ -75,9 +75,14 @@ void file_readline(bool trim, FILE *fp, const function &fn) { } void file_readline(bool trim, const char *file, const function &fn) { - if (auto fp = open_file(file, "re")) - file_readline(trim, fp.get(), fn); + int fd = xopen(file, O_RDONLY | O_CLOEXEC); + if (fd >= 0) { + auto fp = fdopen(fd, "re"); + file_readline(trim, fp, fn); + fclose(fp); + } } + void file_readline(const char *file, const function &fn) { file_readline(false, file, fn); } @@ -96,8 +101,12 @@ void parse_prop_file(FILE *fp, const function &f } void parse_prop_file(const char *file, const function &fn) { - if (auto fp = open_file(file, "re")) - parse_prop_file(fp.get(), fn); + int fd = xopen(file, O_RDONLY | O_CLOEXEC); + if (fd >= 0) { + auto fp = fdopen(fd, "re"); + parse_prop_file(fp, fn); + fclose(fp); + } } std::vector parse_mount_info(const char *pid) { diff --git a/native/src/base/files.rs b/native/src/base/files.rs index 2f5be4d1aec5c..a24ca6110a6e9 100644 --- a/native/src/base/files.rs +++ b/native/src/base/files.rs @@ -2,7 +2,7 @@ use mem::MaybeUninit; use std::cmp::min; use std::ffi::CStr; use std::fs::File; -use std::io::{BufRead, Read, Seek, SeekFrom, Write}; +use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write}; use std::ops::Deref; use std::os::android::fs::MetadataExt; use std::os::fd::{AsFd, BorrowedFd, IntoRawFd}; @@ -12,8 +12,8 @@ use std::{io, mem, ptr, slice}; use bytemuck::{bytes_of_mut, Pod}; use libc::{ - c_uint, dirent, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY, O_RDWR, - O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFREG, + c_uint, dirent, makedev, mode_t, EEXIST, ENOENT, F_OK, O_CLOEXEC, O_CREAT, O_PATH, O_RDONLY, + O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFREG, }; use num_traits::AsPrimitive; @@ -861,3 +861,83 @@ pub(crate) fn map_fd(fd: BorrowedFd, sz: usize, rw: bool) -> io::Result<&'static Ok(slice::from_raw_parts_mut(ptr.cast(), sz)) } } + +#[allow(dead_code)] +pub struct MountInfo { + pub id: u32, + pub parent: u32, + pub device: u64, + pub root: String, + pub target: String, + pub vfs_option: String, + pub shared: u32, + pub master: u32, + pub propagation_from: u32, + pub unbindable: bool, + pub fs_type: String, + pub source: String, + pub fs_option: String, +} + +#[allow(clippy::useless_conversion)] +fn parse_mount_info_line(line: &str) -> Option { + let mut iter = line.split_whitespace(); + let id = iter.next()?.parse().ok()?; + let parent = iter.next()?.parse().ok()?; + let (maj, min) = iter.next()?.split_once(':')?; + let maj = maj.parse().ok()?; + let min = min.parse().ok()?; + let device = makedev(maj, min).into(); + let root = iter.next()?.to_string(); + let target = iter.next()?.to_string(); + let vfs_option = iter.next()?.to_string(); + let mut optional = iter.next()?; + let mut shared = 0; + let mut master = 0; + let mut propagation_from = 0; + let mut unbindable = false; + while optional != "-" { + if let Some(peer) = optional.strip_prefix("master:") { + master = peer.parse().ok()?; + } else if let Some(peer) = optional.strip_prefix("shared:") { + shared = peer.parse().ok()?; + } else if let Some(peer) = optional.strip_prefix("propagate_from:") { + propagation_from = peer.parse().ok()?; + } else if optional == "unbindable" { + unbindable = true; + } + optional = iter.next()?; + } + let fs_type = iter.next()?.to_string(); + let source = iter.next()?.to_string(); + let fs_option = iter.next()?.to_string(); + Some(MountInfo { + id, + parent, + device, + root, + target, + vfs_option, + shared, + master, + propagation_from, + unbindable, + fs_type, + source, + fs_option, + }) +} + +pub fn parse_mount_info(pid: &str) -> Vec { + let mut res = vec![]; + let mut path = format!("/proc/{}/mountinfo", pid); + if let Ok(fd) = open_fd!(Utf8CStr::from_string(&mut path), O_RDONLY | O_CLOEXEC) { + let file = File::from(fd); + BufReader::new(file).foreach_lines(|line| { + parse_mount_info_line(line) + .map(|info| res.push(info)) + .is_some() + }); + } + res +} diff --git a/native/src/base/logging.cpp b/native/src/base/logging.cpp index d0b62a89a719b..683ff8742421c 100644 --- a/native/src/base/logging.cpp +++ b/native/src/base/logging.cpp @@ -8,6 +8,10 @@ using namespace std; +#ifndef __call_bypassing_fortify +#define __call_bypassing_fortify(fn) (&fn) +#endif + #undef vsnprintf static int fmt_and_log_with_rs(LogLevel level, const char *fmt, va_list ap) { constexpr int sz = 4096; diff --git a/native/src/external/Android.mk b/native/src/external/Android.mk index af88e758e37e6..06f4d7356cc38 100644 --- a/native/src/external/Android.mk +++ b/native/src/external/Android.mk @@ -391,3 +391,7 @@ include $(BUILD_STATIC_LIBRARY) CWD := $(LOCAL_PATH) include $(CWD)/system_properties/Android.mk include $(CWD)/libcxx/Android.mk + +ifdef B_CRT0 +include $(CWD)/crt0/Android.mk +endif diff --git a/native/src/external/crt0 b/native/src/external/crt0 new file mode 160000 index 0000000000000..9790b7ee48d3c --- /dev/null +++ b/native/src/external/crt0 @@ -0,0 +1 @@ +Subproject commit 9790b7ee48d3c396c94ef69e164004f50a1f5587 diff --git a/native/src/init/lib.rs b/native/src/init/lib.rs index 7c0aeac2cd7f4..4489406589433 100644 --- a/native/src/init/lib.rs +++ b/native/src/init/lib.rs @@ -1,13 +1,30 @@ +#![feature(format_args_nl)] + use logging::setup_klog; +use mount::{is_device_mounted, switch_root}; +use rootdir::inject_magisk_rc; // Has to be pub so all symbols in that crate is included pub use magiskpolicy; mod logging; +mod mount; +mod rootdir; #[cxx::bridge] pub mod ffi { #[namespace = "rust"] extern "Rust" { fn setup_klog(); + fn inject_magisk_rc(fd: i32, tmp_dir: Utf8CStrRef); + fn switch_root(path: Utf8CStrRef); + fn is_device_mounted(dev: u64, target: Pin<&mut CxxString>) -> bool; + } + + unsafe extern "C++" { + include!("../base/include/base.hpp"); + + #[namespace = "rust"] + #[cxx_name = "Utf8CStr"] + type Utf8CStrRef<'a> = base::ffi::Utf8CStrRef<'a>; } } diff --git a/native/src/init/logging.rs b/native/src/init/logging.rs index c907aa3ec59cd..e38b915b0b42a 100644 --- a/native/src/init/logging.rs +++ b/native/src/init/logging.rs @@ -1,58 +1,54 @@ -use std::fs; use std::fs::File; use std::io::{IoSlice, Write}; -use std::sync::OnceLock; use base::libc::{ - close, makedev, mknod, open, syscall, unlink, SYS_dup3, O_CLOEXEC, O_RDWR, STDERR_FILENO, - STDIN_FILENO, STDOUT_FILENO, S_IFCHR, + makedev, mknod, syscall, SYS_dup3, O_CLOEXEC, O_RDWR, O_WRONLY, STDERR_FILENO, STDIN_FILENO, + STDOUT_FILENO, S_IFCHR, }; -use base::{cstr, exit_on_error, raw_cstr, LogLevel, Logger, Utf8CStr, LOGGER}; +use base::{cstr, exit_on_error, open_fd, raw_cstr, FsPath, LogLevel, Logger, Utf8CStr, LOGGER}; -static KMSG: OnceLock = OnceLock::new(); +// SAFETY: magiskinit is single threaded +static mut KMSG: Option = None; pub fn setup_klog() { - // Shut down first 3 fds unsafe { - let mut fd = open(raw_cstr!("/dev/null"), O_RDWR | O_CLOEXEC); - if fd < 0 { + // Shut down first 3 fds + let mut fd = open_fd!(cstr!("/dev/null"), O_RDWR | O_CLOEXEC); + if fd.is_err() { mknod(raw_cstr!("/null"), S_IFCHR | 0o666, makedev(1, 3)); - fd = open(raw_cstr!("/null"), O_RDWR | O_CLOEXEC); - fs::remove_file("/null").ok(); + fd = open_fd!(cstr!("/null"), O_RDWR | O_CLOEXEC); + FsPath::from(cstr!("/null")).remove().ok(); } - - syscall(SYS_dup3, fd, STDIN_FILENO, O_CLOEXEC); - syscall(SYS_dup3, fd, STDOUT_FILENO, O_CLOEXEC); - syscall(SYS_dup3, fd, STDERR_FILENO, O_CLOEXEC); - if fd > STDERR_FILENO { - close(fd); + if let Ok(ref fd) = fd { + syscall(SYS_dup3, fd, STDIN_FILENO, O_CLOEXEC); + syscall(SYS_dup3, fd, STDOUT_FILENO, O_CLOEXEC); + syscall(SYS_dup3, fd, STDERR_FILENO, O_CLOEXEC); } - } - if let Ok(kmsg) = File::options().write(true).open("/dev/kmsg") { - KMSG.set(kmsg).ok(); - } else { - unsafe { + // Then open kmsg fd + let mut fd = open_fd!(cstr!("/dev/kmsg"), O_WRONLY | O_CLOEXEC); + if fd.is_err() { mknod(raw_cstr!("/kmsg"), S_IFCHR | 0o666, makedev(1, 11)); - KMSG.set(File::options().write(true).open("/kmsg").unwrap()) - .ok(); - unlink(raw_cstr!("/kmsg")); + fd = open_fd!(cstr!("/kmsg"), O_WRONLY | O_CLOEXEC); + FsPath::from(cstr!("/kmsg")).remove().ok(); } + KMSG = fd.map(|fd| fd.into()).ok(); } // Disable kmsg rate limiting - if let Ok(mut rate) = File::options() - .write(true) - .open("/proc/sys/kernel/printk_devkmsg") - { + if let Ok(rate) = open_fd!( + cstr!("/proc/sys/kernel/printk_devkmsg"), + O_WRONLY | O_CLOEXEC + ) { + let mut rate = File::from(rate); writeln!(rate, "on").ok(); } fn kmsg_log_write(_: LogLevel, msg: &Utf8CStr) { - if let Some(kmsg) = KMSG.get().as_mut() { + if let Some(kmsg) = unsafe { &mut KMSG } { let io1 = IoSlice::new("magiskinit: ".as_bytes()); let io2 = IoSlice::new(msg.as_bytes()); - kmsg.write_vectored(&[io1, io2]).ok(); + let _ = kmsg.write_vectored(&[io1, io2]).ok(); } } diff --git a/native/src/init/mount.cpp b/native/src/init/mount.cpp index 476257870ca46..63276901ceba3 100644 --- a/native/src/init/mount.cpp +++ b/native/src/init/mount.cpp @@ -100,29 +100,6 @@ static dev_t setup_block() { return 0; } -static void switch_root(const string &path) { - LOGD("Switch root to %s\n", path.data()); - int root = xopen("/", O_RDONLY); - for (set> mounts; auto &info : parse_mount_info("self")) { - if (info.target == "/" || info.target == path) - continue; - if (auto last_mount = mounts.upper_bound(info.target); - last_mount != mounts.end() && info.target.starts_with(*last_mount + '/')) { - continue; - } - mounts.emplace(info.target); - auto new_path = path + info.target; - xmkdir(new_path.data(), 0755); - xmount(info.target.data(), new_path.data(), nullptr, MS_MOVE, nullptr); - } - chdir(path.data()); - xmount(path.data(), "/", nullptr, MS_MOVE, nullptr); - chroot("."); - - LOGD("Cleaning rootfs\n"); - frm_rf(root); -} - #define PREINITMNT MIRRDIR "/preinit" static void mount_preinit_dir(string preinit_dev) { @@ -137,13 +114,11 @@ static void mount_preinit_dir(string preinit_dev) { xmkdir(PREINITMNT, 0); bool mounted = false; // First, find if it is already mounted - for (auto &info : parse_mount_info("self")) { - if (info.root == "/" && info.device == dev) { - // Already mounted, just bind mount - xmount(info.target.data(), PREINITMNT, nullptr, MS_BIND, nullptr); - mounted = true; - break; - } + std::string mnt_point; + if (rust::is_device_mounted(dev, mnt_point)) { + // Already mounted, just bind mount + xmount(mnt_point.data(), PREINITMNT, nullptr, MS_BIND, nullptr); + mounted = true; } // Since we are mounting the block device directly, make sure to ONLY mount the partitions @@ -212,7 +187,7 @@ bool LegacySARInit::mount_system_root() { } } - switch_root("/system_root"); + rust::switch_root("/system_root"); // Make dev writable xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755"); @@ -243,7 +218,7 @@ void BaseInit::exec_init() { if (xumount2(p.data(), MNT_DETACH) == 0) LOGD("Unmount [%s]\n", p.data()); } - execv("/init", argv); + execve("/init", argv, environ); exit(1); } diff --git a/native/src/init/mount.rs b/native/src/init/mount.rs new file mode 100644 index 0000000000000..147465e22219f --- /dev/null +++ b/native/src/init/mount.rs @@ -0,0 +1,71 @@ +use std::{ + collections::BTreeSet, + ops::Bound::{Excluded, Unbounded}, + pin::Pin, + ptr::null as nullptr, +}; + +use cxx::CxxString; + +use base::{ + cstr, debug, + libc::{chdir, chroot, mount, MS_MOVE}, + parse_mount_info, raw_cstr, Directory, LibcReturn, LoggedResult, StringExt, Utf8CStr, +}; + +pub fn switch_root(path: &Utf8CStr) { + fn inner(path: &Utf8CStr) -> LoggedResult<()> { + debug!("Switch root to {}", path); + let mut mounts = BTreeSet::new(); + let mut rootfs = Directory::open(cstr!("/"))?; + for info in parse_mount_info("self") { + if info.target == "/" || info.target.as_str() == path.as_str() { + continue; + } + if let Some(last_mount) = mounts + .range::((Unbounded, Excluded(&info.target))) + .last() + { + if info.target.starts_with(&format!("{}/", *last_mount)) { + continue; + } + } + let mut new_path = format!("{}/{}", path.as_str(), &info.target); + std::fs::create_dir(&new_path).ok(); + + unsafe { + let mut target = info.target.clone(); + mount( + target.nul_terminate().as_ptr().cast(), + new_path.nul_terminate().as_ptr().cast(), + nullptr(), + MS_MOVE, + nullptr(), + ) + .as_os_err()?; + } + + mounts.insert(info.target); + } + unsafe { + chdir(path.as_ptr()).as_os_err()?; + mount(path.as_ptr(), raw_cstr!("/"), nullptr(), MS_MOVE, nullptr()).as_os_err()?; + chroot(raw_cstr!(".")); + } + + debug!("Cleaning rootfs"); + rootfs.remove_all()?; + Ok(()) + } + inner(path).ok(); +} + +pub fn is_device_mounted(dev: u64, target: Pin<&mut CxxString>) -> bool { + for mount in parse_mount_info("self") { + if mount.root == "/" && mount.device == dev { + target.push_str(&mount.target); + return true; + } + } + false +} diff --git a/native/src/init/rootdir.cpp b/native/src/init/rootdir.cpp index c9b54749a6a24..0fffc8d58f8ec 100644 --- a/native/src/init/rootdir.cpp +++ b/native/src/init/rootdir.cpp @@ -77,24 +77,7 @@ static void patch_rc_scripts(const char *src_path, const char *tmp_path, bool wr rc_list.clear(); // Inject Magisk rc scripts - LOGD("Inject magisk rc\n"); - fprintf(dest.get(), R"EOF( -on post-fs-data - start logd - exec %2$s 0 0 -- %1$s/magisk --post-fs-data - -on property:vold.decrypt=trigger_restart_framework - exec %2$s 0 0 -- %1$s/magisk --service - -on nonencrypted - exec %2$s 0 0 -- %1$s/magisk --service - -on property:sys.boot_completed=1 - exec %2$s 0 0 -- %1$s/magisk --boot-complete - -on property:init.svc.zygote=stopped - exec %2$s 0 0 -- %1$s/magisk --zygote-restart -)EOF", tmp_path, MAGISK_PROC_CON); + rust::inject_magisk_rc(fileno(dest.get()), tmp_path); fclone_attr(fileno(src.get()), fileno(dest.get())); } @@ -403,6 +386,6 @@ int magisk_proxy_main(int argc, char *argv[]) { // Tell magiskd to remount rootfs setenv("REMOUNT_ROOT", "1", 1); - execv("/sbin/magisk", argv); + execve("/sbin/magisk", argv, environ); return 1; } diff --git a/native/src/init/rootdir.rs b/native/src/init/rootdir.rs new file mode 100644 index 0000000000000..4fbe45dee80eb --- /dev/null +++ b/native/src/init/rootdir.rs @@ -0,0 +1,37 @@ +use std::fs::File; +use std::io::Write; +use std::mem; +use std::os::fd::{FromRawFd, RawFd}; + +use base::{debug, Utf8CStr}; + +pub fn inject_magisk_rc(fd: RawFd, tmp_dir: &Utf8CStr) { + debug!("Injecting magisk rc"); + + let mut file = unsafe { File::from_raw_fd(fd) }; + + write!( + file, + r#" +on post-fs-data + start logd + exec {1} 0 0 -- {0}/magisk --post-fs-data + +on property:vold.decrypt=trigger_restart_framework + exec {1} 0 0 -- {0}/magisk --service + +on nonencrypted + exec {1} 0 0 -- {0}/magisk --service + +on property:sys.boot_completed=1 + exec {1} 0 0 -- {0}/magisk --boot-complete + +on property:init.svc.zygote=stopped + exec {1} 0 0 -- {0}/magisk --zygote-restart +"#, + tmp_dir, "u:r:magisk:s0" + ) + .ok(); + + mem::forget(file) +} diff --git a/native/src/sepolicy/include/sepolicy.hpp b/native/src/sepolicy/include/sepolicy.hpp index 836c6ada426ad..69d6f048f99cb 100644 --- a/native/src/sepolicy/include/sepolicy.hpp +++ b/native/src/sepolicy/include/sepolicy.hpp @@ -24,10 +24,7 @@ using token_list = std::vector; using argument = std::pair; using argument_list = std::vector; -const argument &all_xperm(); - #define ALL nullptr -#define ALL_XPERM all_xperm() struct sepolicy { using c_str = const char *; diff --git a/native/src/sepolicy/policydb.cpp b/native/src/sepolicy/policydb.cpp index a3d81c41e538f..30d7f89be1c1c 100644 --- a/native/src/sepolicy/policydb.cpp +++ b/native/src/sepolicy/policydb.cpp @@ -103,7 +103,8 @@ sepolicy *sepolicy::from_file(const char *file) { policy_file_t pf; policy_file_init(&pf); - auto fp = xopen_file(file, "re"); + int fd = xopen(file, O_RDONLY | O_CLOEXEC); + auto fp = make_file(fdopen(fd, "re")); pf.fp = fp.get(); pf.type = PF_USE_STDIO; @@ -123,6 +124,7 @@ sepolicy *sepolicy::compile_split() { cil_db_t *db = nullptr; sepol_policydb_t *pdb = nullptr; FILE *f; + int fd; int policy_ver; const char *cil_file; #if MAGISK_DEBUG @@ -148,13 +150,15 @@ sepolicy *sepolicy::compile_split() { cil_set_target_platform(db, SEPOL_TARGET_SELINUX); cil_set_attrs_expand_generated(db, 1); - f = xfopen(SELINUX_VERSION, "re"); + fd = xopen(SELINUX_VERSION, O_RDONLY | O_CLOEXEC); + f = fdopen(fd, "re"); fscanf(f, "%d", &policy_ver); fclose(f); cil_set_policy_version(db, policy_ver); // Get mapping version - f = xfopen(VEND_POLICY_DIR "plat_sepolicy_vers.txt", "re"); + fd = xopen(VEND_POLICY_DIR "plat_sepolicy_vers.txt", O_RDONLY | O_CLOEXEC); + f = fdopen(fd, "re"); fscanf(f, "%s", plat_ver); fclose(f); diff --git a/native/src/sepolicy/rules.cpp b/native/src/sepolicy/rules.cpp index 3770707031419..68031f0fdfae1 100644 --- a/native/src/sepolicy/rules.cpp +++ b/native/src/sepolicy/rules.cpp @@ -26,9 +26,11 @@ void sepolicy::magisk_rules() { allow(SEPOL_PROC_DOMAIN, ALL, ALL, ALL); // Allow us to do any ioctl if (impl->db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) { - allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", ALL_XPERM); - allowxperm(SEPOL_PROC_DOMAIN, ALL, "fifo_file", ALL_XPERM); - allowxperm(SEPOL_PROC_DOMAIN, ALL, "chr_file", ALL_XPERM); + argument all; + all.first.push_back(nullptr); + allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", all); + allowxperm(SEPOL_PROC_DOMAIN, ALL, "fifo_file", all); + allowxperm(SEPOL_PROC_DOMAIN, ALL, "chr_file", all); } // Create unconstrained file type diff --git a/native/src/sepolicy/statement.cpp b/native/src/sepolicy/statement.cpp index 5e7b488c8850d..e6553c5c2371f 100644 --- a/native/src/sepolicy/statement.cpp +++ b/native/src/sepolicy/statement.cpp @@ -8,13 +8,6 @@ using namespace std; -const argument &all_xperm() { - static argument arg; - if (arg.first.empty()) - arg.first.push_back(nullptr); - return arg; -} - static const char *type_msg_1 = R"EOF("allow *source_type *target_type *class *perm_set" "deny *source_type *target_type *class *perm_set"