Repository navigation
Description
Frida 17.24.3, macOS arm64, frida-core devkit used in-process as the host. Child gating is enabled on a process that
forks (rustc forking its linker).
What happens
- In the fork child,
recreate_agent_thread_after_forkrunsflush_all_sessions. That waits only for each inherited
session'sflush_complete, whichBaseAgentSession.closeresolves beforescript_engine.closeand
transmitter.close(base-agent-session.vala). - The child then sets up its new host connection, registers its
AgentSessionProvider, and calls
WaitForPermissionToResume. On the host,wait_for_permission_to_resumeconnectsprovider.closedto
on_agent_session_provider_closed(host-session-service.vala). - Each inherited session finishes closing afterwards, and the child's provider emits
Closed(id)with the parent's
session ids. on_agent_session_provider_closedremoves the id from the globalagent_sessionsmap and from whichever agent
entry holds it, without checking which provider emitted the signal. That is the parent's entry. When it becomes
empty, the host callsunload_and_destroyon the parent, and the helper frees the agent payload under
UnloadPolicy.IMMEDIATE.- A parent thread that is still inside an intercepted call returns into the freed code and gets SIGSEGV. The client
also sees the parent's sessions as detached (APPLICATION_REQUESTED) although it never detached them.
Evidence (DTrace of the host process's DBus traffic, times relative to the parent's fork):
- +2 ms: parent connection sends
AgentController.RecreateAgentThread - +4 ms: child connection sends
RecreateAgentThread, thenWaitForPermissionToResume - +4 to +5 ms: child connection sends three
AgentSessionProvider.Closedsignals carrying the parent's session ids - +5 ms: host sends
AgentSessionProvider.Unloadon the parent's connection - +10 ms: helper sends
Uninjectedfor the parent
It is timing-dependent: when the child's script close finishes before its new connection exists, the signals are
dropped and nothing happens. In our build it crashes within one to five package builds. I have not yet reduced it to a
standalone script; a short Python probe with one or two sessions closes too quickly to hit the window.
The child-side ordering looks deliberate: d6b79c4 ("Fix child-gating deadlock when script unload blocks") replaced
close_all_clients with flush_all_clients so the child no longer waits for script unload before reconnecting. The
host handler, though, still assumes a session id belongs to only one agent, so the late signals are matched to the
parent. The code is unchanged on main as of 45aef70 and in 17.26.0.
Suggested fix: handle closed only for the entry whose provider emitted it, for example by taking the sender in
on_agent_session_provider_closed (AgentSessionProvider provider, AgentSessionId id) and ignoring ids that the
emitting entry does not own, before touching agent_sessions.
Activity