Fully implement daemon side of Zygisk in Rust

This commit is contained in:
topjohnwu 2025-01-30 00:47:01 +08:00 committed by John Wu
parent b575c95710
commit 15a605765c
12 changed files with 160 additions and 101 deletions

View File

@ -8,6 +8,7 @@ use num_traits::FromPrimitive;
pub use cstr::*;
use cxx_extern::*;
pub use ffi::fork_dont_care;
pub use files::*;
pub use logging::*;
pub use misc::*;
@ -42,6 +43,7 @@ pub mod ffi {
type Utf8CStrRef<'a> = &'a crate::cstr::Utf8CStr;
fn mut_u8_patch(buf: &mut [u8], from: &[u8], to: &[u8]) -> Vec<usize>;
fn fork_dont_care() -> i32;
}
extern "Rust" {

View File

@ -13,8 +13,6 @@
using namespace std;
bool zygisk_enabled = false;
/*********
* Setup *
*********/
@ -175,10 +173,6 @@ bool MagiskD::post_fs_data() const noexcept {
}
exec_common_scripts("post-fs-data");
zygisk_enabled = get_db_setting(DbEntryKey::ZygiskConfig);
initialize_denylist();
setup_mounts();
handle_modules();
return false;
}
@ -206,5 +200,5 @@ void MagiskD::boot_complete() const noexcept {
// Ensure manager exists
get_manager(0, nullptr, true);
reset_zygisk(true);
zygisk_reset(true);
}

View File

@ -142,13 +142,15 @@ static void handle_request_async(int client, int code, const sock_cred &cred) {
case +RequestCode::SUPERUSER:
su_daemon_handler(client, &cred);
break;
case +RequestCode::ZYGOTE_RESTART:
case +RequestCode::ZYGOTE_RESTART: {
LOGI("** zygote restarted\n");
MagiskD().prune_su_access();
auto &daemon = MagiskD();
daemon.prune_su_access();
scan_deny_apps();
reset_zygisk(false);
daemon.zygisk_reset(false);
close(client);
break;
}
case +RequestCode::SQLITE_CMD:
MagiskD().db_exec(client);
break;

View File

@ -1,8 +1,9 @@
use crate::consts::{MAGISK_FULL_VER, MAIN_CONFIG};
use crate::db::Sqlite3;
use crate::ffi::{get_magisk_tmp, ModuleInfo, RequestCode};
use crate::ffi::{get_magisk_tmp, initialize_denylist, DbEntryKey, ModuleInfo, RequestCode};
use crate::get_prop;
use crate::logging::{magisk_logging, start_log_daemon};
use crate::mount::setup_mounts;
use crate::package::ManagerInfo;
use base::libc::{O_CLOEXEC, O_RDONLY};
use base::{
@ -17,6 +18,7 @@ use std::io::{BufReader, ErrorKind, IoSlice, IoSliceMut, Read, Write};
use std::mem::ManuallyDrop;
use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd, RawFd};
use std::os::unix::net::{AncillaryData, SocketAncillary, UnixStream};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Mutex, OnceLock};
// Global magiskd singleton
@ -64,6 +66,9 @@ pub struct MagiskD {
pub manager_info: Mutex<ManagerInfo>,
boot_stage_lock: Mutex<BootStateFlags>,
pub module_list: OnceLock<Vec<ModuleInfo>>,
pub zygiskd_sockets: Mutex<(Option<UnixStream>, Option<UnixStream>)>,
pub zygisk_enabled: AtomicBool,
pub zygote_start_count: AtomicU32,
sdk_int: i32,
pub is_emulator: bool,
is_recovery: bool,
@ -74,6 +79,10 @@ impl MagiskD {
self.is_recovery
}
pub fn zygisk_enabled(&self) -> bool {
self.zygisk_enabled.load(Ordering::Acquire)
}
pub fn sdk_int(&self) -> i32 {
self.sdk_int
}
@ -139,6 +148,14 @@ impl MagiskD {
if check_data() && !state.contains(BootState::PostFsDataDone) {
if self.post_fs_data() {
state.set(BootState::SafeMode);
} else {
self.zygisk_enabled.store(
self.get_db_setting(DbEntryKey::ZygiskConfig) != 0,
Ordering::Release,
);
initialize_denylist();
setup_mounts();
self.handle_modules();
}
state.set(BootState::PostFsDataDone);
}
@ -215,6 +232,7 @@ pub fn daemon_entry() {
sdk_int,
is_emulator,
is_recovery,
zygote_start_count: AtomicU32::new(1),
..Default::default()
};
MAGISKD.set(magiskd).ok();

View File

@ -370,7 +370,7 @@ int enable_deny() {
denylist_enforced = true;
if (!zygisk_enabled) {
if (!MagiskD().zygisk_enabled()) {
if (new_daemon_thread(&logcat)) {
denylist_enforced = false;
return DenyResponse::ERROR;

View File

@ -26,7 +26,6 @@ enum class RespondCode : int {
END
};
extern bool zygisk_enabled;
extern std::string native_bridge;
void reset_zygisk(bool restore);

View File

@ -6,6 +6,8 @@ const char *get_magisk_tmp();
void install_apk(rust::Utf8CStr apk);
void uninstall_pkg(rust::Utf8CStr pkg);
void update_deny_flags(int uid, rust::Str process, uint32_t &flags);
void initialize_denylist();
void restore_zygisk_prop();
// Rust bindings
static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); }

View File

@ -81,6 +81,8 @@ pub mod ffi {
fn install_apk(apk: Utf8CStrRef);
fn uninstall_pkg(apk: Utf8CStrRef);
fn update_deny_flags(uid: i32, process: &str, flags: &mut u32);
fn initialize_denylist();
fn restore_zygisk_prop();
fn switch_mnt_ns(pid: i32) -> i32;
}
@ -186,7 +188,6 @@ pub mod ffi {
fn zygisk_close_logd();
fn zygisk_get_logd() -> i32;
fn setup_logfile();
fn setup_mounts();
fn clean_mounts();
fn find_preinit_device() -> String;
fn revert_unmount(pid: i32);
@ -209,15 +210,16 @@ pub mod ffi {
type MagiskD;
fn is_recovery(&self) -> bool;
fn sdk_int(&self) -> i32;
fn zygisk_enabled(&self) -> bool;
fn boot_stage_handler(&self, client: i32, code: i32);
fn zygisk_handler(&self, client: i32);
fn zygisk_reset(&self, restore: bool);
fn preserve_stub_apk(&self);
fn prune_su_access(&self);
#[cxx_name = "get_manager"]
unsafe fn get_manager_for_cxx(&self, user: i32, ptr: *mut CxxString, install: bool) -> i32;
fn set_module_list(&self, module_list: Vec<ModuleInfo>);
fn module_list(&self) -> &Vec<ModuleInfo>;
fn get_module_fds(&self, is_64_bit: bool) -> Vec<i32>;
#[cxx_name = "get_db_settings"]
fn get_db_settings_for_cxx(&self, cfg: &mut DbSettings) -> bool;
@ -240,11 +242,9 @@ pub mod ffi {
#[allow(dead_code)]
fn reboot(self: &MagiskD);
fn post_fs_data(self: &MagiskD) -> bool;
fn handle_modules(self: &MagiskD);
fn late_start(self: &MagiskD);
fn boot_complete(self: &MagiskD);
#[allow(dead_code)]
fn handle_modules(self: &MagiskD);
fn connect_zygiskd(self: &MagiskD, client: i32);
}
}

View File

@ -269,7 +269,7 @@ static void inject_zygisk_libs(root_node *system) {
}
}
static void load_modules(const rust::Vec<ModuleInfo> &module_list) {
static void load_modules(bool zygisk_enabled, const rust::Vec<ModuleInfo> &module_list) {
node_entry::module_mnt = get_magisk_tmp() + "/"s MODULEMNT "/";
auto root = make_unique<root_node>("");
@ -392,7 +392,7 @@ static void foreach_module(Func fn) {
}
}
static rust::Vec<ModuleInfo> collect_modules(bool open_zygisk) {
static rust::Vec<ModuleInfo> collect_modules(bool zygisk_enabled, bool open_zygisk) {
rust::Vec<ModuleInfo> modules;
foreach_module([&](int dfd, dirent *entry, int modfd) {
if (faccessat(modfd, "remove", F_OK, 0) == 0) {
@ -475,11 +475,12 @@ static rust::Vec<ModuleInfo> collect_modules(bool open_zygisk) {
}
void MagiskD::handle_modules() const noexcept {
bool zygisk = zygisk_enabled();
prepare_modules();
exec_module_scripts("post-fs-data", collect_modules(false));
exec_module_scripts("post-fs-data", collect_modules(zygisk, false));
// Recollect modules (module scripts could remove itself)
auto list = collect_modules(true);
load_modules(list);
auto list = collect_modules(zygisk, true);
load_modules(zygisk, list);
set_module_list(std::move(list));
}

View File

@ -26,7 +26,7 @@ static void set_script_env() {
char new_path[4096];
ssprintf(new_path, sizeof(new_path), "%s:%s", getenv("PATH"), get_magisk_tmp());
setenv("PATH", new_path, 1);
if (zygisk_enabled)
if (MagiskD().zygisk_enabled())
setenv("ZYGISK_ENABLED", "1", 1);
};

View File

@ -1,10 +1,18 @@
use crate::consts::MODULEROOT;
use crate::daemon::{to_user_id, IpcRead, MagiskD, UnixSocketExt};
use crate::ffi::{update_deny_flags, ZygiskRequest, ZygiskStateFlags};
use crate::ffi::{
get_magisk_tmp, restore_zygisk_prop, update_deny_flags, ZygiskRequest, ZygiskStateFlags,
};
use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY};
use base::{cstr, open_fd, Directory, FsPathBuf, LoggedResult, Utf8CStrBufArr, WriteExt};
use base::{
cstr, error, fork_dont_care, libc, open_fd, raw_cstr, warn, Directory, FsPathBuf, LoggedError,
LoggedResult, Utf8CStrBufArr, WriteExt,
};
use std::fmt::Write;
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
use std::os::unix::net::UnixStream;
use std::ptr;
use std::sync::atomic::Ordering;
const UNMOUNT_MASK: u32 =
ZygiskStateFlags::ProcessOnDenyList.repr | ZygiskStateFlags::DenyListEnforced.repr;
@ -22,21 +30,119 @@ impl MagiskD {
};
match code {
ZygiskRequest::GetInfo => self.get_process_info(client)?,
ZygiskRequest::ConnectCompanion => self.connect_zygiskd(client.as_raw_fd()),
ZygiskRequest::ConnectCompanion => self.connect_zygiskd(client),
ZygiskRequest::GetModDir => self.get_mod_dir(client)?,
_ => {}
}
};
}
pub fn get_module_fds(&self, is_64_bit: bool) -> Vec<RawFd> {
if let Some(module_list) = self.module_list.get() {
pub fn zygisk_reset(&self, mut restore: bool) {
if !self.zygisk_enabled.load(Ordering::Acquire) {
return;
}
if restore {
self.zygote_start_count.store(1, Ordering::Release);
} else {
*self.zygiskd_sockets.lock().unwrap() = (None, None);
if self.zygote_start_count.fetch_add(1, Ordering::AcqRel) > 3 {
warn!("zygote crashes too many times, rolling-back");
restore = true;
}
}
if restore {
restore_zygisk_prop();
}
}
fn get_module_fds(&self, is_64_bit: bool) -> Option<Vec<RawFd>> {
self.module_list.get().map(|module_list| {
module_list
.iter()
.map(|m| if is_64_bit { m.z64 } else { m.z32 })
.collect()
} else {
Vec::new()
})
}
fn exec_zygiskd(is_64_bit: bool, remote: UnixStream) {
// This fd has to survive exec
unsafe {
libc::fcntl(remote.as_raw_fd(), libc::F_SETFD, 0);
}
// Start building the exec arguments
let mut exe = Utf8CStrBufArr::<64>::new();
#[cfg(target_pointer_width = "64")]
let magisk = if is_64_bit { "magisk" } else { "magisk32" };
#[cfg(target_pointer_width = "32")]
let magisk = "magisk";
let exe = FsPathBuf::new(&mut exe).join(get_magisk_tmp()).join(magisk);
let mut fd_str = Utf8CStrBufArr::<16>::new();
write!(fd_str, "{}", remote.as_raw_fd()).ok();
unsafe {
libc::execl(
exe.as_ptr(),
raw_cstr!(""),
raw_cstr!("zygisk"),
raw_cstr!("companion"),
fd_str.as_ptr(),
ptr::null() as *const libc::c_char,
);
libc::exit(-1);
}
}
fn connect_zygiskd(&self, mut client: UnixStream) {
let mut zygiskd_sockets = self.zygiskd_sockets.lock().unwrap();
let result: LoggedResult<()> = try {
let is_64_bit = client.ipc_read_int()? != 0;
let socket = if is_64_bit {
&mut zygiskd_sockets.1
} else {
&mut zygiskd_sockets.0
};
if let Some(fd) = socket {
// Make sure the socket is still valid
let mut pfd = libc::pollfd {
fd: fd.as_raw_fd(),
events: 0,
revents: 0,
};
if unsafe { libc::poll(&mut pfd, 1, 0) } != 0 || pfd.revents != 0 {
// Any revent means error
*socket = None;
}
}
let socket = if let Some(fd) = socket {
fd
} else {
// Create a new socket pair and fork zygiskd process
let (local, remote) = UnixStream::pair()?;
if fork_dont_care() == 0 {
Self::exec_zygiskd(is_64_bit, remote);
}
*socket = Some(local);
let local = socket.as_mut().unwrap();
if let Some(module_fds) = self.get_module_fds(is_64_bit) {
local.send_fds(&module_fds)?;
}
if local.ipc_read_int()? != 0 {
Err(LoggedError::default())?;
}
local
};
socket.send_fds(&[client.as_raw_fd()])?;
};
if result.is_err() {
error!("zygiskd startup error");
}
}

View File

@ -30,75 +30,10 @@ extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf {
// The following code runs in magiskd
static pthread_mutex_t zygiskd_lock = PTHREAD_MUTEX_INITIALIZER;
static int zygiskd_sockets[] = { -1, -1 };
#define zygiskd_socket zygiskd_sockets[is_64_bit]
void MagiskD::connect_zygiskd(int client) const noexcept {
mutex_guard g(zygiskd_lock);
bool is_64_bit = read_int(client);
if (zygiskd_socket >= 0) {
// Make sure the socket is still valid
pollfd pfd = { zygiskd_socket, 0, 0 };
poll(&pfd, 1, 0);
if (pfd.revents) {
// Any revent means error
close(zygiskd_socket);
zygiskd_socket = -1;
}
}
if (zygiskd_socket < 0) {
int fds[2];
socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
zygiskd_socket = fds[0];
if (fork_dont_care() == 0) {
char exe[64];
#if defined(__LP64__)
ssprintf(exe, sizeof(exe), "%s/magisk%s", get_magisk_tmp(), (is_64_bit ? "" : "32"));
#else
ssprintf(exe, sizeof(exe), "%s/magisk", get_magisk_tmp());
#endif
// This fd has to survive exec
fcntl(fds[1], F_SETFD, 0);
char buf[16];
ssprintf(buf, sizeof(buf), "%d", fds[1]);
execl(exe, "", "zygisk", "companion", buf, (char *) nullptr);
exit(-1);
}
close(fds[1]);
rust::Vec<int> module_fds = get_module_fds(is_64_bit);
send_fds(zygiskd_socket, rust::Slice<const int>(module_fds));
// Wait for ack
if (read_int(zygiskd_socket) != 0) {
LOGE("zygiskd startup error\n");
return;
}
}
send_fd(zygiskd_socket, client);
}
void reset_zygisk(bool restore) {
if (!zygisk_enabled) return;
static atomic_uint zygote_start_count{1};
if (!restore) {
close(zygiskd_sockets[0]);
close(zygiskd_sockets[1]);
zygiskd_sockets[0] = zygiskd_sockets[1] = -1;
}
if (restore) {
zygote_start_count = 1;
} else if (zygote_start_count.fetch_add(1) > 3) {
LOGW("zygote crashes too many times, rolling-back\n");
restore = true;
}
if (restore) {
string native_bridge_orig = "0";
if (native_bridge.length() > strlen(ZYGISKLDR)) {
native_bridge_orig = native_bridge.substr(strlen(ZYGISKLDR));
}
set_prop(NBPROP, native_bridge_orig.data());
} else {
set_prop(NBPROP, native_bridge.data());
void restore_zygisk_prop() {
string native_bridge_orig = "0";
if (native_bridge.length() > strlen(ZYGISKLDR)) {
native_bridge_orig = native_bridge.substr(strlen(ZYGISKLDR));
}
set_prop(NBPROP, native_bridge_orig.data());
}