Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions loader/src/common/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Module> ReadModules() {
Expand Down
34 changes: 28 additions & 6 deletions loader/src/common/socket_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion loader/src/include/daemon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
27 changes: 16 additions & 11 deletions loader/src/injector/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions module/src/sepolicy.rule
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
11 changes: 7 additions & 4 deletions zygiskd/src/zygiskd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
Loading