From f958e57af64dc5da792b7196cbd4320d76b0366d Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Fri, 3 Nov 2023 18:12:50 +0800 Subject: [PATCH] Get rid of binder-rs & Refine watchdog --- module/src/service.sh | 7 ++ zygiskd/Cargo.toml | 1 - zygiskd/src/constants.rs | 3 + zygiskd/src/utils.rs | 27 +++++++- zygiskd/src/watchdog.rs | 142 ++++++++++++++++++++++++++------------- 5 files changed, 131 insertions(+), 49 deletions(-) diff --git a/module/src/service.sh b/module/src/service.sh index 012a291e..7f18a554 100644 --- a/module/src/service.sh +++ b/module/src/service.sh @@ -9,6 +9,13 @@ fi cd "$MODDIR" +# temporary fix AVD 11 magisk +if [ -f /dev/zygisk_service ];then + log -p i -t "zygisk-sh" "service called twice"; + exit; +fi +touch /dev/zygisk_service + if [ "$(which magisk)" ]; then for file in ../*; do if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then diff --git a/zygiskd/Cargo.toml b/zygiskd/Cargo.toml index 8d7877d1..8c6a6ed3 100644 --- a/zygiskd/Cargo.toml +++ b/zygiskd/Cargo.toml @@ -23,7 +23,6 @@ proc-maps = "0.3" rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] } tokio = { version = "1.28", features = ["full"] } -binder = { git = "https://github.com/Kernel-SU/binder_rs", rev = "c9f2b62d6a744fd2264056c638c1b061a6a2932d" } fuser = { git = "https://github.com/Dr-TSNG/fuser", default-features = false } ptrace-do = { git = "https://github.com/5ec1cff/ptrace-do" } diff --git a/zygiskd/src/constants.rs b/zygiskd/src/constants.rs index f367e90a..06242c0d 100644 --- a/zygiskd/src/constants.rs +++ b/zygiskd/src/constants.rs @@ -40,6 +40,9 @@ pub const STATUS_ROOT_IMPL_TOO_OLD: &str = "❌ Root implementation version too pub const STATUS_ROOT_IMPL_ABNORMAL: &str = "❌ Abnormal root implementation version"; pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations installed"; +pub const MAX_RESTART_COUNT: i32 = 5; +pub const ZYGOTE_SERVICE_PROP: &str = "init.svc.zygote"; + #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[repr(u8)] pub enum DaemonSocketAction { diff --git a/zygiskd/src/utils.rs b/zygiskd/src/utils.rs index 481d36f1..92280fc3 100644 --- a/zygiskd/src/utils.rs +++ b/zygiskd/src/utils.rs @@ -1,6 +1,6 @@ use anyhow::Result; use std::{fs, io::{Read, Write}, os::unix::net::UnixStream}; -use std::ffi::{c_char, CStr, CString}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::os::fd::AsFd; use std::os::unix::net::UnixListener; use std::process::Command; @@ -96,6 +96,28 @@ pub fn set_property(name: &str, value: &str) -> Result<()> { Ok(()) } +pub fn wait_property(name: &str, serial: u32) -> Result { + let name = CString::new(name)?; + let info = unsafe { + __system_property_find(name.as_ptr()) + }; + let mut serial = serial; + unsafe { + __system_property_wait(info, serial, &mut serial, std::ptr::null()); + } + Ok(serial) +} + +pub fn get_property_serial(name: &str) -> Result { + let name = CString::new(name)?; + let info = unsafe { + __system_property_find(name.as_ptr()) + }; + Ok(unsafe { + __system_property_serial(info) + }) +} + pub fn switch_mount_namespace(pid: i32) -> Result<()> { let cwd = std::env::current_dir()?; let mnt = fs::File::open(format!("/proc/{}/ns/mnt", pid))?; @@ -177,4 +199,7 @@ extern "C" { fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32; fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32; fn __system_property_set(name: *const c_char, value: *const c_char) -> u32; + fn __system_property_find(name: *const c_char) -> *const c_void; + fn __system_property_wait(info: *const c_void, old_serial: u32, new_serial: *mut u32, timeout: *const libc::timespec) -> bool; + fn __system_property_serial(info: *const c_void) -> u32; } diff --git a/zygiskd/src/watchdog.rs b/zygiskd/src/watchdog.rs index cd2f38e3..dff6532d 100644 --- a/zygiskd/src/watchdog.rs +++ b/zygiskd/src/watchdog.rs @@ -1,18 +1,17 @@ use crate::{constants, root_impl, utils}; use anyhow::{bail, Result}; use std::fs; -use std::future::Future; use std::io::{BufRead, BufReader, Write}; -use std::pin::Pin; use std::time::Duration; -use binder::IBinder; -use futures::stream::FuturesUnordered; -use futures::StreamExt; +use futures::{FutureExt, join, pin_mut}; +use futures::future::{Fuse, FusedFuture}; use log::{debug, error, info}; use rustix::mount::mount_bind; -use rustix::process::{getgid, getuid, kill_process, Pid, Signal}; +use rustix::process::{getgid, getuid}; use tokio::process::{Child, Command}; -use crate::utils::LateInit; +use tokio::{select, task}; +use tokio::time::Instant; +use crate::utils::{get_property, get_property_serial, LateInit, wait_property}; static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new(); @@ -108,62 +107,111 @@ fn check_and_set_hint() -> Result { } async fn spawn_daemon() -> Result<()> { - let mut lives = 5; + let mut lives = constants::MAX_RESTART_COUNT; + let mut last_restart_time = Instant::now(); + let (sender, mut receiver) = tokio::sync::mpsc::channel(1); + task::spawn_blocking(move || { + let mut serial = 0u32; + let mut last_state = "running".to_string(); + info!("zygote property monitor started"); + loop { + let old_serial = serial; + serial = wait_property(constants::ZYGOTE_SERVICE_PROP, serial).expect("failed to wait on property"); + let new_state = get_property(constants::ZYGOTE_SERVICE_PROP).expect("failed to get property"); + if last_state == "running" && new_state != "running" { + info!("new zygote state: {} serial {} -> {}", new_state, old_serial, serial); + sender.blocking_send(old_serial).expect("failed to notify"); + } + last_state = new_state + } + }); + let mut restart_serial = 0u32; loop { - let mut futures = FuturesUnordered::>>>>::new(); - let mut child_ids = vec![]; let daemon32 = Command::new(constants::PATH_CP_BIN32).arg("daemon").spawn(); let daemon64 = Command::new(constants::PATH_CP_BIN64).arg("daemon").spawn(); - async fn spawn_daemon(mut daemon: Child) -> Result<()> { - let result = daemon.wait().await?; - log::error!("Daemon process {} died: {}", daemon.id().unwrap(), result); - Ok(()) - } - if let Ok(it) = daemon32 { - child_ids.push(it.id().unwrap()); - futures.push(Box::pin(spawn_daemon(it))); - } - if let Ok(it) = daemon64 { - child_ids.push(it.id().unwrap()); - futures.push(Box::pin(spawn_daemon(it))); + async fn spawn_daemon(mut daemon: Child, mut killer: tokio::sync::watch::Receiver<()>) { + let id = daemon.id().unwrap(); + select! { + result = daemon.wait() => { + log::error!("Daemon process {} died: {}", id, result + .expect("failed to get daemon process exit reason") + ); + }, + _ = killer.changed() => { + log::warn!("Kill daemon process {}", id); + daemon.kill().await.expect("failed to kill"); + } + } } + let (tx, rx) = tokio::sync::watch::channel(()); + let wait32 = match daemon32 { + Ok(child) => { + spawn_daemon(child, rx.clone()).fuse() + } + Err(..) => { + Fuse::terminated() + } + }; + let wait64 = match daemon64 { + Ok(child) => { + spawn_daemon(child, rx.clone()).fuse() + } + Err(..) => { + Fuse::terminated() + } + }; - async fn binder_listener() -> Result<()> { - let mut binder = loop { - match binder::get_service("activity") { - Some(binder) => break binder, - None => { - log::trace!("System server not ready, wait for 1s..."); - tokio::time::sleep(Duration::from_secs(1)).await; - } - }; - }; + pin_mut!(wait32, wait64); - info!("System server ready"); + let mut restart_zygote = true; - loop { - if binder.ping_binder().is_err() { break; } - tokio::time::sleep(Duration::from_secs(1)).await; + select! { + _ = &mut wait32 => {}, + _ = &mut wait64 => {}, + _ = async { + // we expect a serial different from last restart + loop { + if restart_serial != receiver.recv().await.expect("no serial received") { + break; + } + } + } => { + restart_zygote = false; } - bail!("System server died"); } - futures.push(Box::pin(binder_listener())); - if let Err(e) = futures.next().await.unwrap() { - error!("{}", e); - } + // kill all remain daemons + tx.send(())?; - for child in child_ids { - debug!("Killing child process {}", child); - let _ = kill_process(Pid::from_raw(child as i32).unwrap(), Signal::Kill); + // wait for all daemons + loop { + futures::select! { + _ = wait32 => {}, + _ = wait64 => {}, + complete => { break; } + } } - lives -= 1; + let current = Instant::now(); + if current - last_restart_time >= Duration::new(30, 0) { + lives = constants::MAX_RESTART_COUNT; + log::warn!("reset live count to {}", lives); + } else { + lives -= 1; + log::warn!("remain live count {}", lives); + } if lives == 0 { bail!("Too many crashes, abort"); } + last_restart_time = current; - error!("Restarting zygote..."); - utils::set_property(constants::PROP_CTL_RESTART, "zygote")?; + error!("Daemons are going to restart ..."); + + if restart_zygote { + error!("Restarting zygote..."); + restart_serial = get_property_serial(constants::ZYGOTE_SERVICE_PROP)?; + debug!("serial before restart {}", restart_serial); + utils::set_property(constants::PROP_CTL_RESTART, "zygote")?; + } } }