diff --git a/loader/src/common/daemon.cpp b/loader/src/common/daemon.cpp index 03b0ded5..deaf943a 100644 --- a/loader/src/common/daemon.cpp +++ b/loader/src/common/daemon.cpp @@ -67,18 +67,31 @@ void CacheMountNamespace(pid_t pid) { socket_utils::write_u32(fd, (uint32_t) pid); } -std::string UpdateMountNamespace(MountNamespace type) { +// Returns the file descriptor >= 0 on success, or -1 on failure. +int UpdateMountNamespace(MountNamespace type) { UniqueFd fd = Connect(1); if (fd == -1) { PLOGE("UpdateMountNamespace"); - return "socket not connected"; + return -1; } socket_utils::write_u8(fd, (uint8_t) SocketAction::UpdateMountNamespace); socket_utils::write_u8(fd, (uint8_t) type); - uint32_t target_pid = socket_utils::read_u32(fd); - int target_fd = (int) socket_utils::read_u32(fd); - if (target_fd == 0) return "not cached yet"; - return "/proc/" + std::to_string(target_pid) + "/fd/" + std::to_string(target_fd); + + // Read Status Byte + uint8_t status = socket_utils::read_u8(fd); + // Handle Failure Case (Not Cached) + if (status == 0) { + // Daemon explicitly told us it doesn't have it. + return -1; + } + // Handle Success Case + int namespace_fd = socket_utils::recv_fd(fd); + if (namespace_fd < 0) { + PLOGE("UpdateMountNamespace: failed to receive fd"); + return -1; + } + + return namespace_fd; } std::vector ReadModules() { diff --git a/loader/src/common/socket_utils.cpp b/loader/src/common/socket_utils.cpp index c1aaf526..6ca41075 100644 --- a/loader/src/common/socket_utils.cpp +++ b/loader/src/common/socket_utils.cpp @@ -69,19 +69,41 @@ void* recv_fds(int sockfd, char* cmsgbuf, size_t bufsz, int cnt) { .msg_flags = 0}; ssize_t rec = xrecvmsg(sockfd, &msg, MSG_WAITALL); - cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - // Validate we received the 4 dummy bytes + // --- IO Failed or Stream Desync --- if (rec != sizeof(dummy_data)) { - return nullptr; + PLOGE("recv_fds: IO failure. Read %zd bytes, expected %zu", rec, sizeof(dummy_data)); } - if (msg.msg_controllen != bufsz || cmsg == nullptr || - cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) || cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + + // --- No headers received --- + if (cmsg == nullptr) { + LOGE("recv_fds: No control headers received. msg_controllen=%zu", + (size_t) msg.msg_controllen); return nullptr; } + // Check msg_controllen <= bufsz + if (msg.msg_controllen != bufsz) { + LOGW("recv_fds: Size mismatch. Buffer capacity: %zu, Received len: %zu", bufsz, + (size_t) msg.msg_controllen); + } + + // Check Header Length Field + size_t expected_cmsg_len = CMSG_LEN(sizeof(int) * cnt); + if (cmsg->cmsg_len != expected_cmsg_len) { + LOGW("recv_fds: CMSG header len mismatch. Header says: %zu, Calculated: %zu (cnt=%d)", + (size_t) cmsg->cmsg_len, expected_cmsg_len, cnt); + } + + // Check Protocol details + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { + LOGW("recv_fds: Protocol mismatch. Level: %d (exp %d), Type: %d (exp %d)", cmsg->cmsg_level, + SOL_SOCKET, cmsg->cmsg_type, SCM_RIGHTS); + } + + // Return data anyway so we can see if the FD is valid return CMSG_DATA(cmsg); } diff --git a/loader/src/include/daemon.hpp b/loader/src/include/daemon.hpp index c13c5885..f88445fd 100644 --- a/loader/src/include/daemon.hpp +++ b/loader/src/include/daemon.hpp @@ -81,7 +81,7 @@ uint32_t GetProcessFlags(uid_t uid); void CacheMountNamespace(pid_t pid); -std::string UpdateMountNamespace(MountNamespace type); +int UpdateMountNamespace(MountNamespace type); int ConnectCompanion(size_t index); diff --git a/loader/src/injector/module.cpp b/loader/src/injector/module.cpp index 940ff007..8d840bb5 100644 --- a/loader/src/injector/module.cpp +++ b/loader/src/injector/module.cpp @@ -486,21 +486,26 @@ void ZygiskContext::nativeForkAndSpecialize_post() { // ----------------------------------------------------------------- bool ZygiskContext::update_mount_namespace(zygiskd::MountNamespace namespace_type) { - LOGV("updating mount namespace to type %s", - namespace_type == zygiskd::MountNamespace::Clean ? "Clean" : "Root"); - std::string ns_path = zygiskd::UpdateMountNamespace(namespace_type); - if (!ns_path.starts_with("/proc/")) { - LOGE("invalid mount namespace path: %s", ns_path.data()); + const char* type_str = (namespace_type == zygiskd::MountNamespace::Clean ? "Clean" : "Root"); + LOGV("updating mount namespace to type %s", type_str); + + int ns_fd = zygiskd::UpdateMountNamespace(namespace_type); + + // Check for failure (Not cached or error) + if (ns_fd < 0) { + LOGW("mount namespace [%s] not available/cached", type_str); return false; } - auto updated_ns = open(ns_path.data(), O_RDONLY); - if (updated_ns >= 0) { - setns(updated_ns, CLONE_NEWNS); - } else { - PLOGE("open mount namespace path [%s]", ns_path.data()); + // Apply the namespace + // setns works directly with the FD received from the socket. + int ret = setns(ns_fd, CLONE_NEWNS); + if (ret != 0) { + PLOGE("setns failed for type %s", type_str); + close(ns_fd); return false; } - close(updated_ns); + + close(ns_fd); return true; } diff --git a/module/src/sepolicy.rule b/module/src/sepolicy.rule index 859f87f9..fefbdac9 100644 --- a/module/src/sepolicy.rule +++ b/module/src/sepolicy.rule @@ -13,3 +13,4 @@ allow zygote zygote process execmem allow system_server system_server process execmem allow zygote tmpfs file * allow zygote appdomain_tmpfs file * +allow zygote proc file * diff --git a/zygiskd/src/zygiskd.rs b/zygiskd/src/zygiskd.rs index 94c81648..c88c41b3 100644 --- a/zygiskd/src/zygiskd.rs +++ b/zygiskd/src/zygiskd.rs @@ -378,13 +378,16 @@ fn handle_get_process_flags(stream: &mut UnixStream) -> Result<()> { fn handle_update_mount_namespace(stream: &mut UnixStream, context: &AppContext) -> Result<()> { let namespace_type = MountNamespace::try_from(stream.read_u8()?)?; - stream.write_u32(unsafe { libc::getpid() } as u32)?; if let Some(fd) = context.mount_manager.get_namespace_fd(namespace_type) { // Namespace is already cached, send the FD to the client. - stream.write_u32(fd as u32)?; + // SUCCESS: Send Status '1', then the FD. + stream.write_u8(1)?; + stream.send_fd(fd)?; } else { - error!("Namespace {:?} is not cached yet.", namespace_type); - stream.write_u32(0)?; + // FAILURE: Send Status '0'. + // Do NOT send an FD or random u32 bytes, just stop here. + warn!("Namespace {:?} is not cached yet.", namespace_type); + stream.write_u8(0)?; } Ok(()) }