diff --git a/native/src/Android.mk b/native/src/Android.mk index 1932de0d6..c3ee3866a 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -29,7 +29,6 @@ LOCAL_SRC_FILES := \ core/scripting.cpp \ core/restorecon.cpp \ core/module.cpp \ - core/logging.cpp \ core/thread.cpp \ resetprop/persist.cpp \ resetprop/resetprop.cpp \ diff --git a/native/src/Cargo.lock b/native/src/Cargo.lock index d71110e2c..7c5d290a6 100644 --- a/native/src/Cargo.lock +++ b/native/src/Cargo.lock @@ -75,7 +75,7 @@ version = "1.0.94" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -183,6 +183,8 @@ version = "0.0.0" dependencies = [ "base", "cxx", + "num-derive", + "num-traits", ] [[package]] @@ -217,6 +219,26 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -332,6 +354,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.15" @@ -373,7 +406,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] diff --git a/native/src/base/consts.rs b/native/src/base/consts.rs new file mode 100644 index 000000000..d8ce98f2f --- /dev/null +++ b/native/src/base/consts.rs @@ -0,0 +1,9 @@ +// We expose constant values as macros so that all constants are literals +// An advantage for doing this is that we can use concat!() on these values + +#[macro_export] +macro_rules! LOGFILE { + () => { + "/cache/magisk.log" + }; +} diff --git a/native/src/base/lib.rs b/native/src/base/lib.rs index 88fffa40f..a1ddc7cc8 100644 --- a/native/src/base/lib.rs +++ b/native/src/base/lib.rs @@ -2,11 +2,13 @@ pub use libc; +pub use consts::*; pub use files::*; pub use logging::*; pub use misc::*; pub use xwrap::*; +mod consts; mod files; mod logging; mod misc; diff --git a/native/src/base/misc.hpp b/native/src/base/misc.hpp index f4e16f6f7..7fc5ad8cb 100644 --- a/native/src/base/misc.hpp +++ b/native/src/base/misc.hpp @@ -130,7 +130,7 @@ uint64_t parse_uint64_hex(std::string_view s); int parse_int(std::string_view s); using thread_entry = void *(*)(void *); -int new_daemon_thread(thread_entry entry, void *arg = nullptr); +extern "C" int new_daemon_thread(thread_entry entry, void *arg = nullptr); static inline bool str_contains(std::string_view s, std::string_view ss) { return s.find(ss) != std::string::npos; diff --git a/native/src/base/misc.rs b/native/src/base/misc.rs index f835dd032..811baa348 100644 --- a/native/src/base/misc.rs +++ b/native/src/base/misc.rs @@ -151,3 +151,24 @@ pub unsafe fn slice_from_ptr_mut<'a, T>(buf: *mut T, len: usize) -> &'a mut [T] slice::from_raw_parts_mut(buf, len) } } + +pub trait FlatData { + fn as_raw_bytes(&self) -> &[u8] + where + Self: Sized, + { + unsafe { + let self_ptr = self as *const Self as *const u8; + slice::from_raw_parts(self_ptr, std::mem::size_of::()) + } + } + fn as_raw_bytes_mut(&mut self) -> &mut [u8] + where + Self: Sized, + { + unsafe { + let self_ptr = self as *mut Self as *mut u8; + slice::from_raw_parts_mut(self_ptr, std::mem::size_of::()) + } + } +} diff --git a/native/src/core/Cargo.toml b/native/src/core/Cargo.toml index bd7422be2..5d7ed4208 100644 --- a/native/src/core/Cargo.toml +++ b/native/src/core/Cargo.toml @@ -10,3 +10,5 @@ path = "lib.rs" [dependencies] base = { path = "../base" } cxx = { path = "../external/cxx-rs" } +num-traits = "0.2" +num-derive = "0.3" diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp index 4a7d56264..d0ed61373 100644 --- a/native/src/core/bootstages.cpp +++ b/native/src/core/bootstages.cpp @@ -373,7 +373,7 @@ static void post_fs_data() { if (!check_data()) return; - setup_logfile(true); + rust::get_magiskd().setup_logfile(); LOGI("** post-fs-data mode running\n"); @@ -413,7 +413,7 @@ early_abort: } static void late_start() { - setup_logfile(false); + rust::get_magiskd().setup_logfile(); LOGI("** late_start service mode running\n"); @@ -425,7 +425,7 @@ static void late_start() { static void boot_complete() { boot_state |= FLAG_BOOT_COMPLETE; - setup_logfile(false); + rust::get_magiskd().setup_logfile(); LOGI("** boot-complete triggered\n"); diff --git a/native/src/core/core.hpp b/native/src/core/core.hpp index b0a2d4016..c90e3c07b 100644 --- a/native/src/core/core.hpp +++ b/native/src/core/core.hpp @@ -9,8 +9,6 @@ extern std::atomic pkg_xml_ino; std::string find_preinit_device(); void unlock_blocks(); void reboot(); -void start_log_daemon(); -void setup_logfile(bool reset); std::string read_certificate(int fd, int version = -1); // Module stuffs diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index ba0f28ee9..8bedb6954 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -186,7 +186,7 @@ static void handle_request_sync(int client, int code) { write_string(client, MAGISKTMP.data()); break; case MainRequest::START_DAEMON: - setup_logfile(true); + rust::get_magiskd().setup_logfile(); break; case MainRequest::STOP_DAEMON: denylist_handler(-1, nullptr); @@ -298,7 +298,7 @@ static void switch_cgroup(const char *cgroup, int pid) { } static void daemon_entry() { - magisk_logging(); + android_logging(); // Block all signals sigset_t block_set; @@ -321,7 +321,7 @@ static void daemon_entry() { setsid(); setcon(MAGISK_PROC_CON); - start_log_daemon(); + rust::daemon_entry(); LOGI(NAME_WITH_VER(Magisk) " daemon started\n"); diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs new file mode 100644 index 000000000..9aac813b5 --- /dev/null +++ b/native/src/core/daemon.rs @@ -0,0 +1,31 @@ +use crate::logging::{magisk_logging, zygisk_logging}; +use std::cell::RefCell; +use std::fs::File; +use std::sync::{Mutex, OnceLock}; + +// Global magiskd singleton +pub static MAGISKD: OnceLock = OnceLock::new(); + +#[derive(Default)] +pub struct MagiskD { + pub logd: Mutex>>, +} + +pub fn daemon_entry() { + let magiskd = MagiskD::default(); + magiskd.start_log_daemon(); + MAGISKD.set(magiskd).ok(); + magisk_logging(); +} + +pub fn zygisk_entry() { + let magiskd = MagiskD::default(); + MAGISKD.set(magiskd).ok(); + zygisk_logging(); +} + +pub fn get_magiskd() -> &'static MagiskD { + MAGISKD.get().unwrap() +} + +impl MagiskD {} diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index 7b6b01cff..23ed17682 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -1,6 +1,8 @@ pub use base; +use daemon::*; use logging::*; +mod daemon; mod logging; #[cxx::bridge] @@ -13,4 +15,18 @@ pub mod ffi { } } +#[cxx::bridge(namespace = "rust")] +pub mod ffi2 { + extern "Rust" { + fn daemon_entry(); + fn zygisk_entry(); + + type MagiskD; + fn get_magiskd() -> &'static MagiskD; + fn get_log_pipe(self: &MagiskD) -> i32; + fn close_log_pipe(self: &MagiskD); + fn setup_logfile(self: &MagiskD); + } +} + fn rust_test_entry() {} diff --git a/native/src/core/logging.cpp b/native/src/core/logging.cpp deleted file mode 100644 index d0032b69e..000000000 --- a/native/src/core/logging.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -#include "core.hpp" - -using namespace std; - -struct log_meta { - int prio; - int len; - int pid; - int tid; -}; - -atomic logd_fd = -1; - -void setup_logfile(bool reset) { - if (logd_fd < 0) - return; - - iovec iov{}; - log_meta meta = { - .prio = -1, - .len = reset - }; - - iov.iov_base = &meta; - iov.iov_len = sizeof(meta); - writev(logd_fd, &iov, 1); -} - -// Maximum message length for pipes to transfer atomically -#define MAX_MSG_LEN (int) (PIPE_BUF - sizeof(log_meta)) - -static void *logfile_writer(void *arg) { - int pipefd = (long) arg; - - run_finally close_pipes([=] { - // Close up all logging pipes when thread dies - close(pipefd); - close(logd_fd.exchange(-1)); - }); - - struct { - void *data; - size_t len; - } tmp{}; - stream_ptr strm = make_unique(tmp.data, tmp.len); - bool switched = false; - - log_meta meta{}; - char buf[MAX_MSG_LEN]; - char aux[64]; - - iovec iov[2]; - iov[0].iov_base = aux; - iov[1].iov_base = buf; - - for (;;) { - // Read meta data - if (read(pipefd, &meta, sizeof(meta)) != sizeof(meta)) - return nullptr; - - if (meta.prio < 0) { - if (!switched) { - run_finally free_tmp([&] { - free(tmp.data); - tmp.data = nullptr; - tmp.len = 0; - }); - - rename(LOGFILE, LOGFILE ".bak"); - int fd = open(LOGFILE, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0644); - if (fd < 0) - return nullptr; - if (tmp.data) - write(fd, tmp.data, tmp.len); - - strm = make_unique(fd); - switched = true; - } - continue; - } - - // Read message - if (read(pipefd, buf, meta.len) != meta.len) - return nullptr; - - timeval tv; - tm tm; - gettimeofday(&tv, nullptr); - localtime_r(&tv.tv_sec, &tm); - - // Format detailed info - char type; - switch (meta.prio) { - case ANDROID_LOG_DEBUG: - type = 'D'; - break; - case ANDROID_LOG_INFO: - type = 'I'; - break; - case ANDROID_LOG_WARN: - type = 'W'; - break; - default: - type = 'E'; - break; - } - long ms = tv.tv_usec / 1000; - size_t off = strftime(aux, sizeof(aux), "%m-%d %T", &tm); - off += ssprintf(aux + off, sizeof(aux) - off, - ".%03ld %5d %5d %c : ", ms, meta.pid, meta.tid, type); - - iov[0].iov_len = off; - iov[1].iov_len = meta.len; - - strm->writev(iov, 2); - } -} - -void magisk_log_write(int prio, const char *msg, int len) { - if (logd_fd < 0) - return; - - // Truncate - len = std::min(MAX_MSG_LEN, len); - - log_meta meta = { - .prio = prio, - .len = len, - .pid = getpid(), - .tid = gettid() - }; - - iovec iov[2]; - iov[0].iov_base = &meta; - iov[0].iov_len = sizeof(meta); - iov[1].iov_base = (void *) msg; - iov[1].iov_len = len; - - if (writev(logd_fd, iov, 2) < 0) { - // Stop trying to write to file - close(logd_fd.exchange(-1)); - } -} - -void start_log_daemon() { - int fds[2]; - if (pipe2(fds, O_CLOEXEC) == 0) { - logd_fd = fds[1]; - long fd = fds[0]; - new_daemon_thread(logfile_writer, (void *) fd); - } -} diff --git a/native/src/core/logging.rs b/native/src/core/logging.rs index 02a1fcdde..8b5a092af 100644 --- a/native/src/core/logging.rs +++ b/native/src/core/logging.rs @@ -1,8 +1,27 @@ -use base::ffi::LogLevel; -use base::*; +use std::cmp::min; +use std::ffi::{c_char, c_void}; use std::fmt::Arguments; +use std::fs::File; +use std::io::{IoSlice, Read, Write}; +use std::os::fd::{AsRawFd, FromRawFd, RawFd}; +use std::ptr::null_mut; +use std::{fs, io}; + +use num_derive::{FromPrimitive, ToPrimitive}; +use num_traits::FromPrimitive; + +use base::ffi::LogLevel; +use base::libc::{ + getpid, gettid, gettimeofday, localtime_r, pipe2, pthread_sigmask, sigaddset, sigset_t, + sigtimedwait, timespec, timeval, tm, O_CLOEXEC, PIPE_BUF, SIGPIPE, SIG_BLOCK, SIG_SETMASK, +}; +use base::*; + +use crate::daemon::{MagiskD, MAGISKD}; +use crate::logging::LogFile::{Actual, Buffer}; #[allow(dead_code, non_camel_case_types)] +#[derive(FromPrimitive, ToPrimitive)] #[repr(i32)] enum ALogPriority { ANDROID_LOG_UNKNOWN = 0, @@ -16,10 +35,14 @@ enum ALogPriority { ANDROID_LOG_SILENT, } +type ThreadEntry = extern "C" fn(*mut c_void) -> *mut c_void; + extern "C" { - fn __android_log_write(prio: i32, tag: *const u8, msg: *const u8); - fn magisk_log_write(prio: i32, msg: *const u8, len: i32); - fn zygisk_log_write(prio: i32, msg: *const u8, len: i32); + fn __android_log_write(prio: i32, tag: *const c_char, msg: *const u8); + fn strftime(buf: *mut c_char, len: usize, fmt: *const c_char, tm: *const tm) -> usize; + + fn zygisk_fetch_logd() -> RawFd; + fn new_daemon_thread(entry: ThreadEntry, arg: *mut c_void); } fn level_to_prio(level: LogLevel) -> i32 { @@ -37,12 +60,12 @@ pub fn android_logging() { let mut buf: [u8; 4096] = [0; 4096]; fmt_to_buf(&mut buf, args); unsafe { - __android_log_write(level_to_prio(level), b"Magisk\0".as_ptr(), buf.as_ptr()); + __android_log_write(level_to_prio(level), str_ptr!("Magisk"), buf.as_ptr()); } } fn android_log_write(level: LogLevel, msg: &[u8]) { unsafe { - __android_log_write(level_to_prio(level), b"Magisk\0".as_ptr(), msg.as_ptr()); + __android_log_write(level_to_prio(level), str_ptr!("Magisk"), msg.as_ptr()); } } @@ -62,15 +85,15 @@ pub fn magisk_logging() { let mut buf: [u8; 4096] = [0; 4096]; let len = fmt_to_buf(&mut buf, args); unsafe { - __android_log_write(level_to_prio(level), b"Magisk\0".as_ptr(), buf.as_ptr()); - magisk_log_write(level_to_prio(level), buf.as_ptr(), len as i32); + __android_log_write(level_to_prio(level), str_ptr!("Magisk"), buf.as_ptr()); } + magisk_log_write(level_to_prio(level), &buf[..len]); } fn magisk_write(level: LogLevel, msg: &[u8]) { unsafe { - __android_log_write(level_to_prio(level), b"Magisk\0".as_ptr(), msg.as_ptr()); - magisk_log_write(level_to_prio(level), msg.as_ptr(), msg.len() as i32); + __android_log_write(level_to_prio(level), str_ptr!("Magisk"), msg.as_ptr()); } + magisk_log_write(level_to_prio(level), &msg); } let logger = Logger { @@ -89,15 +112,15 @@ pub fn zygisk_logging() { let mut buf: [u8; 4096] = [0; 4096]; let len = fmt_to_buf(&mut buf, args); unsafe { - __android_log_write(level_to_prio(level), b"Magisk\0".as_ptr(), buf.as_ptr()); - zygisk_log_write(level_to_prio(level), buf.as_ptr(), len as i32); + __android_log_write(level_to_prio(level), str_ptr!("Magisk"), buf.as_ptr()); } + zygisk_log_write(level_to_prio(level), &buf[..len]); } fn zygisk_write(level: LogLevel, msg: &[u8]) { unsafe { - __android_log_write(level_to_prio(level), b"Magisk\0".as_ptr(), msg.as_ptr()); - zygisk_log_write(level_to_prio(level), msg.as_ptr(), msg.len() as i32); + __android_log_write(level_to_prio(level), str_ptr!("Magisk"), msg.as_ptr()); } + zygisk_log_write(level_to_prio(level), &msg); } let logger = Logger { @@ -110,3 +133,250 @@ pub fn zygisk_logging() { LOGGER = logger; } } + +#[derive(Default)] +#[repr(C)] +struct LogMeta { + prio: i32, + len: i32, + pid: i32, + tid: i32, +} + +const MAX_MSG_LEN: usize = PIPE_BUF - std::mem::size_of::(); + +fn do_magisk_log_write(logd: &mut File, prio: i32, msg: &[u8]) -> io::Result { + // Truncate message if needed + let len = min(MAX_MSG_LEN, msg.len()); + let msg = &msg[..len]; + + let meta = LogMeta { + prio, + len: len as i32, + pid: unsafe { getpid() }, + tid: unsafe { gettid() }, + }; + + let io1 = IoSlice::new(meta.as_raw_bytes()); + let io2 = IoSlice::new(msg); + logd.write_vectored(&[io1, io2]) +} + +fn magisk_log_write(prio: i32, msg: &[u8]) { + let magiskd = match MAGISKD.get() { + None => return, + Some(s) => s, + }; + + let logd_cell = magiskd.logd.lock().unwrap(); + let mut logd_ref = logd_cell.borrow_mut(); + let logd = match logd_ref.as_mut() { + None => return, + Some(s) => s, + }; + + let result = do_magisk_log_write(logd, prio, &msg); + + // If any error occurs, shut down the logd pipe + if result.is_err() { + *logd_ref = None; + } +} + +fn zygisk_log_write(prio: i32, msg: &[u8]) { + let magiskd = match MAGISKD.get() { + None => return, + Some(s) => s, + }; + + let logd_cell = magiskd.logd.lock().unwrap(); + let mut logd_ref = logd_cell.borrow_mut(); + if logd_ref.is_none() { + android_logging(); + unsafe { + let fd = zygisk_fetch_logd(); + if fd < 0 { + return; + } + *logd_ref = Some(File::from_raw_fd(fd)); + } + // Only re-enable zygisk logging if success + zygisk_logging(); + }; + let logd = logd_ref.as_mut().unwrap(); + + // Block SIGPIPE + let mut mask: sigset_t; + let mut orig_mask: sigset_t; + unsafe { + mask = std::mem::zeroed(); + orig_mask = std::mem::zeroed(); + sigaddset(&mut mask, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &mask, &mut orig_mask); + } + + let result = do_magisk_log_write(logd, prio, &msg); + + // Consume SIGPIPE if exists, then restore mask + unsafe { + let mut ts: timespec = std::mem::zeroed(); + sigtimedwait(&mask, null_mut(), &ts); + pthread_sigmask(SIG_SETMASK, &orig_mask, null_mut()); + } + + // If any error occurs, shut down the logd pipe + if result.is_err() { + *logd_ref = None; + } +} + +// The following is implementation for the logging daemon + +enum LogFile<'a> { + Buffer(&'a mut Vec), + Actual(File), +} + +impl LogFile<'_> { + fn as_write(&mut self) -> &mut dyn Write { + match self { + Buffer(e) => e, + Actual(ref mut e) => e, + } + } +} + +impl FlatData for LogMeta {} + +extern "C" fn logfile_writer(arg: *mut c_void) -> *mut c_void { + fn writer_loop(pipefd: RawFd) -> io::Result<()> { + let mut pipe = unsafe { File::from_raw_fd(pipefd) }; + let mut tmp = Vec::new(); + let mut logfile: LogFile = Buffer(&mut tmp); + + let mut meta = LogMeta::default(); + let mut buf: [u8; MAX_MSG_LEN] = [0; MAX_MSG_LEN]; + let mut aux: [u8; 64] = [0; 64]; + + loop { + // Read request + pipe.read_exact(meta.as_raw_bytes_mut())?; + + if meta.prio < 0 { + if matches!(logfile, LogFile::Buffer(_)) { + fs::rename(LOGFILE!(), concat!(LOGFILE!(), ".bak")).ok(); + let mut out = File::create(LOGFILE!())?; + out.write_all(tmp.as_slice())?; + tmp = Vec::new(); + logfile = Actual(out); + } + continue; + } + + if meta.len < 0 || meta.len > buf.len() as i32 { + continue; + } + + // Read the rest of the message + let msg = &mut buf[..(meta.len as usize)]; + pipe.read_exact(msg)?; + + // Start building the log string + + let prio = + ALogPriority::from_i32(meta.prio).unwrap_or(ALogPriority::ANDROID_LOG_UNKNOWN); + let prio = match prio { + ALogPriority::ANDROID_LOG_VERBOSE => 'V', + ALogPriority::ANDROID_LOG_DEBUG => 'D', + ALogPriority::ANDROID_LOG_INFO => 'I', + ALogPriority::ANDROID_LOG_WARN => 'W', + ALogPriority::ANDROID_LOG_ERROR => 'E', + // Unsupported values, skip + _ => continue, + }; + + // Note: the obvious better implementation is to use the rust chrono crate, however + // the crate cannot fetch the proper local timezone without pulling in a bunch of + // timezone handling code. To reduce final binary size, fallback to use libc. + let mut aux_len: usize; + unsafe { + let mut tv: timeval = std::mem::zeroed(); + let mut tm: tm = std::mem::zeroed(); + gettimeofday(&mut tv, null_mut()); + localtime_r(&tv.tv_sec, &mut tm); + aux_len = strftime( + aux.as_mut_ptr().cast(), + aux.len(), + str_ptr!("%m-%d %T"), + &tm, + ) as usize; + let ms = tv.tv_usec / 1000; + aux_len += bfmt!( + &mut aux[aux_len..], + ".{:03} {:5} {:5} {} : ", + ms, + meta.pid, + meta.tid, + prio + ); + } + + let io1 = IoSlice::new(&aux[..aux_len]); + let io2 = IoSlice::new(msg); + logfile.as_write().write_vectored(&[io1, io2])?; + } + } + + writer_loop(arg as RawFd).ok(); + // If any error occurs, shut down the logd pipe + if let Some(magiskd) = MAGISKD.get() { + magiskd.close_log_pipe(); + } + null_mut() +} + +impl MagiskD { + pub fn start_log_daemon(&self) { + let mut fds: [i32; 2] = [0; 2]; + unsafe { + if pipe2(fds.as_mut_ptr(), O_CLOEXEC) == 0 { + let logd = self.logd.lock().unwrap(); + *logd.borrow_mut() = Some(File::from_raw_fd(fds[1])); + new_daemon_thread(logfile_writer, fds[0] as *mut c_void); + } + } + } + + pub fn get_log_pipe(&self) -> RawFd { + let logd_cell = self.logd.lock().unwrap(); + let logd_ref = logd_cell.borrow(); + let logd = logd_ref.as_ref(); + match logd { + None => -1, + Some(s) => s.as_raw_fd(), + } + } + + pub fn close_log_pipe(&self) { + let guard = self.logd.lock().unwrap(); + *guard.borrow_mut() = None; + } + + pub fn setup_logfile(&self) { + let logd_cell = self.logd.lock().unwrap(); + let mut logd_ref = logd_cell.borrow_mut(); + let logd = match logd_ref.as_mut() { + None => return, + Some(s) => s, + }; + + let meta = LogMeta { + prio: -1, + len: 0, + pid: 0, + tid: 0, + }; + + logd.write_all(meta.as_raw_bytes()).ok(); + } +} diff --git a/native/src/include/daemon.hpp b/native/src/include/daemon.hpp index b90559fd9..f58a23be5 100644 --- a/native/src/include/daemon.hpp +++ b/native/src/include/daemon.hpp @@ -83,10 +83,6 @@ void clear_poll(); // Thread pool void exec_task(std::function &&task); -// Logging -extern std::atomic logd_fd; -extern "C" void magisk_log_write(int prio, const char *msg, int len); - // Daemon handlers void boot_stage_handler(int client, int code); void denylist_handler(int client, const sock_cred *cred); diff --git a/native/src/zygisk/entry.cpp b/native/src/zygisk/entry.cpp index 10bf019fc..becbf1166 100644 --- a/native/src/zygisk/entry.cpp +++ b/native/src/zygisk/entry.cpp @@ -41,7 +41,7 @@ extern "C" void unload_first_stage() { } extern "C" void zygisk_inject_entry(void *handle) { - zygisk_logging(); + rust::zygisk_entry(); ZLOGD("load success\n"); char *ld = getenv("LD_PRELOAD"); @@ -62,7 +62,7 @@ extern "C" void zygisk_inject_entry(void *handle) { // The following code runs in zygote/app process -extern "C" void zygisk_log_write(int prio, const char *msg, int len) { +extern "C" int zygisk_fetch_logd() { // If we don't have the log pipe set, request magiskd for it. This could actually happen // multiple times in the zygote daemon (parent process) because we had to close this // file descriptor to prevent crashing. @@ -77,37 +77,18 @@ extern "C" void zygisk_log_write(int prio, const char *msg, int len) { // add this FD into fds_to_ignore to pass the check. For other cases, we accomplish this by // hooking __android_log_close and closing it at the same time as the rest of logging FDs. - if (logd_fd < 0) { - android_logging(); - if (int fd = zygisk_request(ZygiskRequest::GET_LOG_PIPE); fd >= 0) { - int log_pipe = -1; - if (read_int(fd) == 0) { - log_pipe = recv_fd(fd); - } - close(fd); - if (log_pipe >= 0) { - // Only re-enable zygisk logging if possible - logd_fd = log_pipe; - zygisk_logging(); - } - } else { - return; + if (int fd = zygisk_request(ZygiskRequest::GET_LOG_PIPE); fd >= 0) { + int log_pipe = -1; + if (read_int(fd) == 0) { + log_pipe = recv_fd(fd); + } + close(fd); + if (log_pipe >= 0) { + return log_pipe; } } - // Block SIGPIPE - sigset_t mask; - sigset_t orig_mask; - sigemptyset(&mask); - sigaddset(&mask, SIGPIPE); - pthread_sigmask(SIG_BLOCK, &mask, &orig_mask); - - magisk_log_write(prio, msg, len); - - // Consume SIGPIPE if exists, then restore mask - timespec ts{}; - sigtimedwait(&mask, nullptr, &ts); - pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr); + return -1; } static inline bool should_load_modules(uint32_t flags) { @@ -340,7 +321,7 @@ static void get_process_info(int client, const sock_cred *cred) { } static void send_log_pipe(int fd) { - // There is race condition here, but we can't really do much about it... + int logd_fd = rust::get_magiskd().get_log_pipe(); if (logd_fd >= 0) { write_int(fd, 0); send_fd(fd, logd_fd); diff --git a/native/src/zygisk/hook.cpp b/native/src/zygisk/hook.cpp index 9c96060a0..7e9c94b1f 100644 --- a/native/src/zygisk/hook.cpp +++ b/native/src/zygisk/hook.cpp @@ -20,6 +20,8 @@ using namespace std; using jni_hook::hash_map; using jni_hook::tree_map; using xstring = jni_hook::string; +using rust::MagiskD; +using rust::get_magiskd; // Extreme verbose logging //#define ZLOGV(...) ZLOGD(__VA_ARGS__) @@ -67,6 +69,7 @@ struct HookContext { AppSpecializeArgs_v3 *app; ServerSpecializeArgs_v1 *server; } args; + const MagiskD &magiskd; const char *process; list modules; @@ -94,7 +97,7 @@ struct HookContext { vector ignore_info; HookContext(JNIEnv *env, void *args) : - env(env), args{args}, process(nullptr), pid(-1), info_flags(0), + env(env), args{args}, magiskd(get_magiskd()), process(nullptr), pid(-1), info_flags(0), hook_info_lock(PTHREAD_MUTEX_INITIALIZER) { static bool restored_env = false; if (!restored_env) { @@ -171,9 +174,9 @@ DCL_HOOK_FUNC(int, unshare, int flags) { DCL_HOOK_FUNC(void, android_log_close) { if (g_ctx == nullptr) { // Happens during un-managed fork like nativeForkApp, nativeForkUsap - close(logd_fd.exchange(-1)); + get_magiskd().close_log_pipe(); } else if (!g_ctx->flags[SKIP_FD_SANITIZATION]) { - close(logd_fd.exchange(-1)); + g_ctx->magiskd.close_log_pipe(); if (g_ctx->is_child()) { // Switch to plain old android logging because we cannot talk // to magiskd to fetch our log pipe afterwards anyways. @@ -484,6 +487,7 @@ void HookContext::sanitize_fds() { int dfd = dirfd(dir.get()); for (dirent *entry; (entry = xreaddir(dir.get()));) { int fd = parse_int(entry->d_name); + int logd_fd = magiskd.get_log_pipe(); if ((fd < 0 || fd >= MAX_FD_SIZE || !allowed_fds[fd]) && fd != dfd && fd != logd_fd) { close(fd); } @@ -570,7 +574,7 @@ void HookContext::app_specialize_post() { // Cleanups env->ReleaseStringUTFChars(args.app->nice_name, process); - close(logd_fd.exchange(-1)); + magiskd.close_log_pipe(); android_logging(); } @@ -690,8 +694,11 @@ void HookContext::nativeForkAndSpecialize_pre() { // if fds_to_ignore does not exist and there's no FileDescriptorTable::Create, // we can skip fd sanitization flags[SKIP_FD_SANITIZATION] = !dlsym(RTLD_DEFAULT, "_ZN19FileDescriptorTable6CreateEv"); - } else if (logd_fd >= 0) { - exempted_fds.push_back(logd_fd); + } else { + int logd_fd = magiskd.get_log_pipe(); + if (logd_fd >= 0) { + exempted_fds.push_back(logd_fd); + } } fork_pre();