diff --git a/loader/src/common/dl.cpp b/loader/src/common/dl.cpp index 4f85cb0..eb5f7d2 100644 --- a/loader/src/common/dl.cpp +++ b/loader/src/common/dl.cpp @@ -44,8 +44,17 @@ void* DlopenExt(const char* path, int flags) { return handle; } -void* DlopenMem(int memfd, int flags) { - char path[PATH_MAX]; - sprintf(path, "/proc/self/fd/%d", memfd); - return DlopenExt(path, flags); +void* DlopenMem(int fd, int flags) { + auto info = android_dlextinfo{ + .flags = ANDROID_DLEXT_USE_LIBRARY_FD, + .library_fd = fd + }; + + auto* handle = android_dlopen_ext("/jit-cache", flags, &info); + if (handle) { + LOGD("dlopen fd %d: %p", fd, handle); + } else { + LOGE("dlopen fd %d: %s", fd, dlerror()); + } + return handle; } diff --git a/loader/src/include/daemon.h b/loader/src/include/daemon.h index f93874f..faab188 100644 --- a/loader/src/include/daemon.h +++ b/loader/src/include/daemon.h @@ -20,7 +20,7 @@ public: UniqueFd(Fd fd) : fd_(fd) {} - ~UniqueFd() { close(fd_); } + ~UniqueFd() { if (fd_ >= 0) close(fd_); } // Disallow copy UniqueFd(const UniqueFd&) = delete; diff --git a/module/build.gradle.kts b/module/build.gradle.kts index 9b6ca60..1e9391a 100644 --- a/module/build.gradle.kts +++ b/module/build.gradle.kts @@ -45,8 +45,8 @@ androidComponents.onVariants { variant -> include("module.prop") expand( "moduleId" to moduleId, - "moduleName" to moduleName, - "versionName" to "$verName ($verCode)", + "moduleName" to "$moduleName-$variantCapped", + "versionName" to "$verName ($verCode)-($variantCapped)", "versionCode" to verCode, ) } diff --git a/module/src/customize.sh b/module/src/customize.sh index 1232092..4cf9eb9 100644 --- a/module/src/customize.sh +++ b/module/src/customize.sh @@ -80,6 +80,11 @@ extract "$ZIPFILE" 'customize.sh' "$TMPDIR/.vunzip" extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip" extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR" +if [ "$DEBUG" = true ]; then + ui_print "- Add debug SELinux policy" + echo "allow crash_dump adb_data_file dir search" >> "$TMPDIR/sepolicy.rule" +fi + if [ "$KSU" ]; then ui_print "- Checking SELinux patches" if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then diff --git a/zygiskd/Cargo.toml b/zygiskd/Cargo.toml index 54f90d0..ad4424b 100644 --- a/zygiskd/Cargo.toml +++ b/zygiskd/Cargo.toml @@ -14,7 +14,7 @@ konst = "0.3.4" lazy_static = "1.4.0" log = "0.4.17" memfd = "0.6.2" -nix = "0.26.2" +nix = { version = "0.26.2", features = ["process","poll"] } num_enum = "0.5.9" once_cell = "1.17.1" passfd = "0.1.5" diff --git a/zygiskd/src/utils.rs b/zygiskd/src/utils.rs index ea43288..72733bb 100644 --- a/zygiskd/src/utils.rs +++ b/zygiskd/src/utils.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{bail, ensure, Result}; use nix::unistd::gettid; use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command}; use std::ffi::c_char; diff --git a/zygiskd/src/zygiskd.rs b/zygiskd/src/zygiskd.rs index 86d36d5..25a5b0d 100644 --- a/zygiskd/src/zygiskd.rs +++ b/zygiskd/src/zygiskd.rs @@ -1,7 +1,7 @@ use crate::constants::DaemonSocketAction; -use crate::utils::UnixStreamExt; +use crate::utils::{UnixStreamExt}; use crate::{constants, debug_select, lp_select, magic, root_impl, utils}; -use anyhow::{bail, Result}; +use anyhow::{bail, ensure, Result}; use memfd::Memfd; use nix::{ fcntl::{fcntl, FcntlArg, FdFlag}, @@ -11,6 +11,7 @@ use passfd::FdPassingExt; use std::sync::{Arc, Mutex}; use std::thread; use std::fs; +use std::os::fd::{IntoRawFd, OwnedFd, RawFd}; use std::os::unix::{ net::{UnixListener, UnixStream}, prelude::AsRawFd, @@ -18,10 +19,13 @@ use std::os::unix::{ use std::os::unix::process::CommandExt; use std::path::PathBuf; use std::process::Command; +use nix::poll::{poll, PollFd, PollFlags}; +use nix::sys::wait::{waitpid, WaitStatus}; +use nix::unistd::{fork, ForkResult}; struct Module { name: String, - memfd: Memfd, + memfd: OwnedFd, companion: Mutex>, } @@ -91,32 +95,30 @@ fn load_modules(arch: &str) -> Result> { continue; } log::info!(" Loading module `{name}`..."); - let memfd = match create_memfd(&so_path, &name) { - Ok(memfd) => memfd, + let fd = match create_library_fd(&so_path) { + Ok(fd) => fd, Err(e) => { log::warn!(" Failed to create memfd for `{name}`: {e}"); continue; } }; - let companion = match spawn_companion(&name, &memfd) { - Ok(companion) => companion, - Err(e) => { - log::warn!(" Failed to spawn companion for `{name}`: {e}"); - continue; - } - }; - - let companion = Mutex::new(companion); - let module = Module { name, memfd, companion }; + let companion = Mutex::new(None); + let module = Module { name, memfd: fd, companion }; modules.push(module); } Ok(modules) } -fn create_memfd(so_path: &PathBuf, debug_name: &str) -> Result { +#[cfg(debug_assertions)] +fn create_library_fd(so_path: &PathBuf) -> Result { + Ok(OwnedFd::from(fs::File::open(so_path)?)) +} + +#[cfg(not(debug_assertions))] +fn create_library_fd(so_path: &PathBuf) -> Result { let opts = memfd::MemfdOptions::default().allow_sealing(true); - let memfd = opts.create(debug_select!(debug_name, "jit-cache"))?; + let memfd = opts.create("jit-cache")?; let file = fs::File::open(so_path)?; let mut reader = std::io::BufReader::new(file); let mut writer = memfd.as_file(); @@ -129,7 +131,7 @@ fn create_memfd(so_path: &PathBuf, debug_name: &str) -> Result { seals.insert(memfd::FileSeal::SealSeal); memfd.add_seals(&seals)?; - Ok(memfd) + Ok(OwnedFd::from(memfd.into_file())) } fn create_daemon_socket() -> Result { @@ -141,22 +143,36 @@ fn create_daemon_socket() -> Result { Ok(listener) } -fn spawn_companion(name: &str, memfd: &Memfd) -> Result> { +fn spawn_companion(name: &str, fd: &RawFd) -> Result> { let (mut daemon, companion) = UnixStream::pair()?; // Remove FD_CLOEXEC flag fcntl(companion.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?; let process = std::env::args().next().unwrap(); let nice_name = process.split('/').last().unwrap(); - Command::new(&process) - .arg0(format!("{}-{}", nice_name, name)) - .arg("companion") - .arg(format!("{}", companion.as_raw_fd())) - .spawn()?; - drop(companion); + + match unsafe { fork()? } { + ForkResult::Parent { child, ..} => { + if let Ok(WaitStatus::Exited(.., code)) = waitpid(child, None) { + ensure!(code == 0, format!("process exited with {code}")); + } else { + bail!("process exited abnormally"); + } + } + ForkResult::Child => { + Command::new(&process) + .arg0(format!("{}-{}", nice_name, name)) + .arg("companion") + .arg(format!("{}", companion.as_raw_fd())) + .spawn()?; + drop(companion); + + std::process::exit(0); + } + } daemon.write_string(name)?; - daemon.send_fd(memfd.as_raw_fd())?; + daemon.send_fd(*fd)?; match daemon.read_u8()? { 0 => Ok(None), 1 => Ok(Some(daemon)), @@ -213,12 +229,33 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()> DaemonSocketAction::RequestCompanionSocket => { let index = stream.read_usize()?; let module = &context.modules[index]; + let name = &module.name; + let fd = &module.memfd; let mut companion = module.companion.lock().unwrap(); + if let Some(sock) = companion.as_ref() { + let mut pfds = [PollFd::new(sock.as_raw_fd(), PollFlags::empty())]; + poll(&mut pfds, 0)?; + if !pfds[0].revents().unwrap().is_empty() { + log::error!("poll companion for module `{}` crashed", name); + companion.take(); + } + } + if companion.as_ref().is_none() { + match spawn_companion(&name, &fd.as_raw_fd()) { + Ok(c) => { + log::trace!(" spawned companion for `{name}`"); + *companion = c; + }, + Err(e) => { + log::warn!(" Failed to spawn companion for `{name}`: {e}"); + } + }; + } match companion.as_ref() { Some(sock) => { if let Err(_) = sock.send_fd(stream.as_raw_fd()) { - log::error!("Companion of module `{}` crashed", module.name); - companion.take(); + log::error!("Companion socket of module `{}` missing", module.name); + stream.write_u8(0)?; } // Ok: Send by companion