Skip to content

Pair Button + CFNetwork Crash Analysis

Date: 2026-04-12 (CFNetwork part RESOLVED, Pair button part still relevant) Status: CFNetwork crash fixed. Pair button mechanism described below is the path forward but needs to be implemented via the right protocol layer.

Part 1: CFNetwork Crash — RESOLVED

Original problem

CDS crashed with SIGSEGV in HTTPConnectionCache::timeoutIdleConnections() when service connection actions ran. NULL+0x68 deref inside CFNetwork.

Root cause

interpose_xpc_remote_connection_create_with_remote_service was returning NULL for any service not in our hardcoded list of 10 services. CFNetwork registered NULL in HTTPConnectionCache and crashed during the idle-cleanup timer.

Fix applied

Parse all 74 services from the RSD Handshake Services dict into g_services[] at init time. The interpose then has a real port for any service Xcode asks for, and never returns NULL. See iosmux_xpc_proxy.m Handshake event handler.

This part of the document is kept for historical context only — the bug is gone.

Part 2: Pair Button Mechanism — STILL RELEVANT

Problem

Xcode shows "Pair" button even when DeviceInfo.pairingState is set to .paired. Clicking it triggers an XPC action flow that we currently can't satisfy.

Mechanism (verified by DVTCoreDeviceCore disassembly)

Xcode UI displays the device based on a computed deviceWindowCategory property in DVTDeviceKit, which depends on three KVO properties of DVTCoreDevice_Impl:

deviceWindowCategory =
    if hasConnection then connected_path (category 3 or 6 → no Pair button)
    else                  disconnected_path (category 0 or 4 → shows Pair button)

hasConnection is computed:

hasConnection = (_shadowUseAssertion != nil &&
                 _shadowUseAssertion.state == .fulfilled)

_shadowUseAssertion is a CoreDevice.DeviceUsageAssertion? field on DVTCoreDevice_Impl. It is set when Xcode's local CoreDevice.RemoteDevice.acquireDeviceUsageAssertion(withReason:options:...) returns success — which dispatches an AcquireBUsageAssertionActionDeclaration through Mercury to CDS and waits for the reply.

Without a successful response to that action, _shadowUseAssertion stays nil → hasConnection is false → device window category is "disconnected" → Xcode shows the Pair button.

Implication

To make the Pair button disappear, we need CDS to respond successfully to the acquireusageassertion action. The exact response format is the CoreDevice.AcquireBUsageAssertionActionDeclaration.Output type — Codable struct, internal fields not yet runtime-verified.

Action interception complication

Xcode↔CDS uses Mercury Codable XPCDictionary with mangledTypeName envelope, NOT the flat CoreDevice.featureIdentifier dict format that pymobiledevice3 uses against the iPhone. Filtering by a top-level string key is impossible on this connection. See action-interception-full-picture.md for the synthesis and the new strategy options.

Part 3: Missing DeviceInfo Fields — UNVERIFIED HYPOTHESIS

We currently set: name, productType, hardwareModel, marketingName, state, pairingState, visibilityClass, areDeveloperDiskImageServicesAvailable, preparednessState.

The following fields may also affect Xcode's device categorization. The list comes from disassembling DVTCoreDeviceCore.DVTCoreDevice_Impl setters and matching them to CoreDeviceProtocols.DeviceInfo fields. None of these have been runtime-verified to actually change Xcode behavior.

Field Plausible value Why it might matter
transportType .localNetwork Auto-pair + connection logic
platform .iOS UI classification
deviceType .iPhone UI icon/category
reality .physical vs virtual/simulated
osVersion from Handshake UI display
osBuild from Handshake Compatibility checks
udid from Handshake Identity cross-check
authenticationType .manualPairing Pairing flow type
developerModeStatus .enabled Skip developer mode prompt
bootState .booted Device is running
isMobileDeviceOnly true iOS device flag

All of OSVersion / BuildVersion / UniqueDeviceID / ProductType / SerialNumber / DeviceClass / HardwarePlatform / ChipID / BoardId / UniqueChipID are available in the RSD Handshake Properties dict (46 keys total).

When to set these

Defer until after action interception is working. If acquireusageassertion returning success is enough to make the Pair button disappear and CDS to consider the device usable, the additional DeviceInfo fields may not be needed. If it isn't enough, set them one at a time and observe.