8000 Host unloads a forking parent's agent when the fork child's inherited sessions close · Issue #1287 · frida/frida-core · GitHub
[go: up one dir, main page]

Skip to content

Host unloads a forking parent's agent when the fork child's inherited sessions close #1287

Open

Description

@cowwoc

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

  1. In the fork child, recreate_agent_thread_after_fork runs flush_all_sessions. That waits only for each inherited
    session's flush_complete, which BaseAgentSession.close resolves before script_engine.close and
    transmitter.close (base-agent-session.vala).
  2. The child then sets up its new host connection, registers its AgentSessionProvider, and calls
    WaitForPermissionToResume. On the host, wait_for_permission_to_resume connects provider.closed to
    on_agent_session_provider_closed (host-session-service.vala).
  3. Each inherited session finishes closing afterwards, and the child's provider emits Closed(id) with the parent's
    session ids.
  4. on_agent_session_provider_closed removes the id from the global agent_sessions map 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 calls unload_and_destroy on the parent, and the helper frees the agent payload under
    UnloadPolicy.IMMEDIATE.
  5. 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, then WaitForPermissionToResume
  • +4 to +5 ms: child connection sends three AgentSessionProvider.Closed signals carrying the parent's session ids
  • +5 ms: host sends AgentSessionProvider.Unload on the parent's connection
  • +10 ms: helper sends Uninjected for 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0