Summary
Two concurrent RSD/tunnel sessions to the same device fail. When one command holds an RSD session open (e.g. ios devicestate enable, which keeps a condition active until it receives SIGTERM), a second command that opens its own RSD session (e.g. ios devicestate list, ios info display, ios ps) fails to connect with an RST / STREAM_CLOSED error — or, intermittently, the second command "succeeds" but the state operation behaves as if it never connected.
Steps to reproduce
With the tunnel running (ios tunnel start), Developer Mode on, and a DDI mounted (iOS 17+):
# Terminal 1 — holds an RSD/instruments session open
ios devicestate enable SlowNetworkCondition SlowNetwork3GGood --udid=$UDID
# Terminal 2 — a second RSD command while the first is running
ios devicestate list --udid=$UDID
Actual result
The second command fails:
{"err":"newRsdServiceFromTcpConn: failed to create xpc connection: CreateXpcConnection: failed to initialize xpc connection: initializeXpcConnection: failed to decode message: DecodeMessage: failed to read magic number: ReadClientServerStream: readDataFrame: got RST frame with error code: STREAM_CLOSED","level":"fatal","msg":"could not connect to RSD, host fd23:4326:be9c::1, port 49911"}
On macOS it sometimes manifests differently: the second command connects but the operation does not reflect reality (e.g. devicestate list reports IsActive=false even though the condition is active).
Expected result
Concurrent RSD-based commands against the same device/tunnel should work, or at least fail gracefully / queue, rather than resetting the stream.
Notes
- Reproduced on both macOS and Linux self-hosted runners.
- iOS 18.4.1 (iPhone14,2) and iOS 18.5 (iPhone15,2).
- Tunnel created via
ios tunnel start (kernel tunnel).
- Interestingly, spacing the commands out by a few seconds and only issuing the second one once often works, so it looks like a race/contention in establishing a second RSD/XPC session rather than a hard "only one session ever" limit.
Impact
Makes it hard to verify state-mutating tunnel commands by querying device state from a separate process while the setter holds its session (e.g. confirming devicestate enable took effect via a concurrent devicestate list). A workaround is to read the setter command's own output instead of issuing a concurrent query.
Summary
Two concurrent RSD/tunnel sessions to the same device fail. When one command holds an RSD session open (e.g.
ios devicestate enable, which keeps a condition active until it receives SIGTERM), a second command that opens its own RSD session (e.g.ios devicestate list,ios info display,ios ps) fails to connect with anRST / STREAM_CLOSEDerror — or, intermittently, the second command "succeeds" but the state operation behaves as if it never connected.Steps to reproduce
With the tunnel running (
ios tunnel start), Developer Mode on, and a DDI mounted (iOS 17+):Actual result
The second command fails:
{"err":"newRsdServiceFromTcpConn: failed to create xpc connection: CreateXpcConnection: failed to initialize xpc connection: initializeXpcConnection: failed to decode message: DecodeMessage: failed to read magic number: ReadClientServerStream: readDataFrame: got RST frame with error code: STREAM_CLOSED","level":"fatal","msg":"could not connect to RSD, host fd23:4326:be9c::1, port 49911"}On macOS it sometimes manifests differently: the second command connects but the operation does not reflect reality (e.g.
devicestate listreportsIsActive=falseeven though the condition is active).Expected result
Concurrent RSD-based commands against the same device/tunnel should work, or at least fail gracefully / queue, rather than resetting the stream.
Notes
ios tunnel start(kernel tunnel).Impact
Makes it hard to verify state-mutating tunnel commands by querying device state from a separate process while the setter holds its session (e.g. confirming
devicestate enabletook effect via a concurrentdevicestate list). A workaround is to read the setter command's own output instead of issuing a concurrent query.