Q-D66-15 history — D21..D33 (2026-04-30..2026-05-01)¶
Status: verified — historical archive
Q-D66-15 Resolution log entries for D21 (V2 verdict), D22 (P-1a
static disasm), D23 (P-1a deploy partial), D24 (W4 common funnel),
D25-D29 (FORGE invalidation + 0/N baseline), D30 (AFM peer
keep-alive failed), D31 (peer[1013] race + SUCCESS path observed),
D32 (kernel-driven invalidation), D33 (devicectl injection
feasibility). Archived from
d66-research-questions.md on
2026-05-02 anchor refactor.
Q-D66-15 — RESOLVED, 2026-04-30 (D21): V2 — post-reply peer/capability check via side-channel peer[2019] invalidation¶
D21 lldb dynamic instrumentation closed the V1/V2/V3 trichotomy from
D20 §verdict. Two trigger runs were captured: v1 attached lldb to
CoreDeviceService (per the original brief assumption); v2
re-architected the probe after observing zero bp hits in v1 — the AUA
async wrapper at CD+0x31750 runs in the client-side devicectl
process address space, NOT in CoreDeviceService. This is the load-
bearing observation that required the brief's CDS-attach approach to
be replaced mid-probe.
V1 (pre-await sanity check) falsified. AUA_ENTRY at CD+0x31750
fires on devicectl Task 1 (queue
com.apple.root.default-qos.cooperative) and resolves to symbol
CoreDevice.RemoteDevice.acquireDeviceUsageAssertion(withReason:options:) async throws -> CoreDevice.DeviceUsageAssertion.
The XPC reply success(...) carrying our inject-synthesised echo
arrives at 11:36:13.294488 (v1 unified.log L74-78) and the function
proceeds past entry — a pre-await abort would not allow either.
V3 (post-reply timeout adapter) falsified. The post-AUA
THROW_RESUME backtrace contains exclusively
_dispatch_call_block_and_release / _dispatch_lane_serial_drain /
_dispatch_lane_invoke / _dispatch_root_queue_drain_deferred_wlh /
_dispatch_workloop_worker_thread frames — zero
dispatch_source_* or dispatch_after_* frames that V3 would
require. The 11.266 ms gap from XPC-reply to errorCode-3 emission
(v1 timing, lldb-untouched inferior) is also too short for any
realistic timeout.
V2 (post-reply peer/capability check) confirmed. Three independent empirical citations:
- The throw fires from a named, dedicated serial dispatch queue
com.apple.dt.coredevice.remotedevice.default.assertionqondevicectlthread #4 (NOT the original Task 1 cooperative thread), with frame chainswift_continuation_throwingResume → CD+0x31b502 (___30eb0+1538?) → CD+0x34cbd (___3bc80+61) → CD+0x316a2 (___30eb0+2034) → CD+0x17319 (___1e300+25) → _dispatch_call_block_and_release. The throw originates from inside___lldb_unnamed_symbol_30eb0 + 2034 = CD+0x316a2, the SAME containing function whose+857offset is the brief's failure-resume landmarkCD+0x31209— but ~1177 bytes deeper in a different basic block. - Under v2 lldb-slowed timing, the failure manifests as
XPCError(errorCode: 1001, "The connection was invalidated.", ... peer[2019].0x...)from a side-channel peer connection to CDS pid 2019. This is direct evidence of a SECOND XPC connection running parallel to the action-reply channel; when that side-channel closes, the assertionq observes the invalidated peer-state and throws. - Both the AUA-context throw and an earlier non-AUA Mercury throw
(ProvisioningProvidersListRequest path on default-qos) carry the
identical Swift-error vtable
0x00007ffe42111e70with errorCode 3 payload — uniform error structure consistent with V2's prediction that the wrapper synthesises a canonicalCoreDeviceError(errorCode: 3)from the post-reply check rather than each path constructing a unique error.
Brief landmark refinement (D22 must use these instead of D20-era guesses):
CD+0x31750— AUA wrapper entry — CONFIRMED (still authoritative).CD+0x31a49— claimed_swift_continuation_awaitcall site — NOT on the executed path; resolves to___lldb_unnamed_symbol_317a0 + 681but never fires under primary trigger.CD+0x31209— claimed failure-resume xref — NOT the actual throw site; resolves to___lldb_unnamed_symbol_30eb0 + 857but never fires.CD+0x316a2— THE ACTUAL throw site —___lldb_unnamed_symbol_30eb0 + 2034. D22's static disasm work begins here.
Apparatus integrity confirmed: 7/7 SHA256 matches across
CoreDevice / CoreDeviceService / CoreDeviceUtilities / inject-dylib
variants / devicectl, no DiagnosticReports/.ips files, lldb detached
cleanly. Pre-attach manifest at
/home/op/backups/iosmux/d21-pre-attach/manifest-source.txt (89
entries, 70 unique sha256, 470 files total under cat-a/+cat-b/+cat-c/).
Full evidence: notes/d21-aua-wrapper-probe.md §A-§R (gitignored,
in-repo). 18 sections with raw register dumps, stack walks, memory
dumps, unified-log timing, disassembly context. Cited verbatim per
ADR-0006. Architectural distillation that survives compaction:
docs/research/coredevice-internals/aua-side-channel-mechanism.md.
Implication for D22: P-1 (highest confidence) — hold side-channel
XPC peer connection to CDS open beyond action reply emit. Either
CDS-side hook on Mercury side-channel transport, or devicectl-side mock
peer. P-2 (medium) — NOP throw call at CD+0x316a2 or substitute
swift_continuation_resume with synthesised DeviceUsageAssertion
(risk: Swift type metadata mismatch crash). P-3 (low) — redirect
side-channel destination to inject-resident fake-listener; requires
side-channel-establishment understanding (currently a §Q open). Six
new gaps surfaced for D22+ scope, catalogued in notes/d21-*.md §Q.
Q-D66-15 — D22 (2026-04-30, static disasm): P-1a winning vector + corrections to D21 interpretation¶
D22 full static disasm of ___lldb_unnamed_symbol_30eb0,
___lldb_unnamed_symbol_1e300, ___lldb_unnamed_symbol_3bc80,
___lldb_unnamed_symbol_324a0 (all four functions D21 saw in the
throw chain), plus xref scan of CoreDevice / Mercury / CoreDeviceUtilities
binaries for side-channel transport symbols. Full evidence in
notes/d22-side-channel-disasm.md §A-§N. Apparatus integrity 7/7
sha256 verified non-destructive.
Critical corrections to D21 interpretation (V2 verdict stands; mechanism localised more precisely):
-
___30eb0 + 2034is NOT a throw site. Full disasm (505 lines) shows___30eb0is a plain C-ABI Swift result-completion dispatcher for the callback-based variantacquireDeviceUsageAssertion(...invokingCompletionHandlerOn:completion:)at CD+0x304a0. NO_swift_continuation_*calls, NOxpc_*calls, NOdispatch_async, NO peer-state predicate inside. The +2034 instruction is the return PC aftercall qword ptr [rbp - 0xa0]at +2028 (the SUCCESS-path completion-callback invocation; ERROR path's analogous call is at +1908). -
D21 frame addresses contained transcription errors. lldb symbol naming convention:
___lldb_unnamed_symbol_NNNNNIS the hexadecimal file offset of the function's head. So: ___3bc80 + 61=0x3bc80 + 0x3d= CD+0x3bcbd (D21 said 0x34cbd)-
___324a0 + 98=0x324a0 + 0x62= CD+0x32502 (D21 said 0x31b502) -
Vtable
0x00007ffe42111e70istaskHeapMetadata + 24, not a CoreDeviceError vtable. Resolved via Step 9 lldb attach to CDS pid 2019 —libswift_Concurrency.dylib.__DATA_DIRTY.__data + 1216. This is the Swift Concurrency runtime metadata for task-heap- allocated objects; D21's "uniform CoreDeviceError carrier" interpretation was incorrect. -
Real throw mechanism: the success-callback at +2028 invokes Swift code (likely a
withCheckedThrowingContinuationadapter bridging the callback API to the async wrapper) which synchronously enters Mercury'sXPCSideChannel-installed handler chain. When the cached peer connection has been invalidated, the handler observesXPCError(errorCode: 1001, "The connection was invalidated.")(Mercury'sXPCError.connectionInvalidconstant at Mercury+0x56e70) and converts it toCoreDeviceError(errorCode: 3)which is then thrown back through the awaiter chain.
Localised V2 mechanism — eviction at ActionConnectionCache:
the cached peer-connection that the in-flight assertion holds is
evicted by _$s10CoreDevice21ActionConnectionCacheC25removeCachedXPCConnection_02toB12IdentifiedByy7Mercury013RemoteXPCPeerD0C_10Foundation4UUIDVtF
at CD+0xdbe0 between (a) the action reply and (b) the post-reply
assertion-confirmation step. Hooking this one symbol to no-op
prevents premature eviction. The cache key is (deviceUUID, feature),
populated by getOrCreateRemoteXPCConnection* at CD+0xf700 (closure
variant) and CD+0x108c0 (async variant). The endpoint→peer-connection
conversion happens inside Mercury's RemoteXPCConnection.unsafePeer(from:)
at Mercury+0x3eca0, not in CoreDevice itself.
Fix vector verdict: P-1a wins. ~30-50 LoC inject code
extending inject/iosmux_inject.m (or new inject/iosmux_aua_keepalive.m),
using the existing GAMBIT mach_vm_protect inline-patch pattern.
Replacement bytes: C3 90 90 90 90 (single ret + nop padding —
function returns void). Preserves ADR-0009 bridge mode. 4-6 hours
including build, deploy, primary-trigger validation. Memory side-
effect: leaks one Mercury peer-connection per AUA call (~MB-scale
growth over hours, acceptable).
Fallback within P-1a (if NOP-evict insufficient): wrap
_$s7Mercury14XPCSideChannelC15setEventHandler... at Mercury+0x1cae0
to filter XPCError.connectionInvalid (1001) from the user-supplied
event handler.
P-2a empirically infeasible: there is no
swift_continuation_throwingResume call inside ___30eb0 to NOP.
The brief premise was wrong. NOP-ing the success-callback at +2028
would orphan the continuation and hang devicectl forever.
P-1b (devicectl-side mock peer) and P-1c (hybrid) rejected as overkill: 12-26h for new injection mechanism with no demonstrated need beyond P-1a. Reserved as escalations.
P-2b rejected: ADR-0009 §Decision violation (would lie when
iPhone genuinely returned error) + high ABI-fragility risk
(synthesising DeviceUsageAssertion Obj-C class with
_lockedMutableState that crashes on null).
Implication for D23: implement P-1a primary approach. D23 brief authored next.
Q-D66-15 — D23 (2026-04-30, P-1a deploy): PARTIAL success — XPCError 1001 eliminated, residual gate revealed¶
D23 implemented and deployed P-1a NOP-evict on
ActionConnectionCache.removeCachedXPCConnection at CD+0xdbe0.
New TU inject/iosmux_aua_keepalive.m (113 LoC) + inject/Makefile
update committed in f591aa1 and pushed.
Empirically validated on havoc:
- Build sha256:
52df2cc643dbc6c24d3debd6eba8682ded9fd8cf5d1f9ee3c57962e0171b4803(193664 B; was 188944 B on Phase 1 baseline) - Runtime hook log:
[inject] AUA keepalive: removeCachedXPCConnection NOP installed at 0x102ebcbe0 rc=0 - XPCError 1001 ELIMINATED from action-XPC connection eviction path (verified by
grep -E "1001|connectionInvalid" /tmp/iosmux-d23-unified.log→ 0 matches at this site) - Trigger improvements: exit code 0 (was non-zero);
xcrun devicectl device info detailsnow returns FULL device info (productType, hardwareModel, marketingName, hostnames, capabilities, tunnelState=connected) - Apparatus integrity: 4/4 system binaries (CoreDevice, CoreDeviceService, CoreDeviceUtilities, devicectl) sha256 UNCHANGED. iPhone connected. Zero CDS crashes.
- dlsym duplicate-export discovery: CoreDevice export trie has TWO entries with mangled name
removeCachedXPCConnection(at 0xdbe0 AND 0xe088). dlsym fails on ambiguity. v1 build (dlsym-only) returnedrc=-1; v2 build (image-walk + hardcoded D22 0xdbe0 offset) returnedrc=0. ADR-0006 satisfied — offset is empirically grounded in D22 §J.
Residual failure: Error: Failed to acquire assertion still in stdout AND CoreDeviceError(errorCode: 3) still in unified log, but on a SEPARATE downstream path that fires AFTER the action success() reply and analytics emit.
D23 agent halted on Stop Condition #6 (different call path than D22's V2-path). The brief's Step 12 fallback (filter XPCError.connectionInvalid (1001) via Mercury XPCSideChannel.setEventHandler wrap) was specifically designed against the cache-eviction-driven 1001; post-D23, 1001 from that path is gone. Filter would have nothing to filter — agent properly surfaced rather than executing theatre.
Implication for D24: dynamic re-probe to localise the new gate's instantiation site. Apparatus already in optimal post-D23 state.
Q-D66-15 — D24 (2026-04-30, post-D23 dynamic re-probe): W4 verdict — common funnel with side-channel peer as second input¶
D24 lldb dynamic re-probe via launch-devicectl-under-lldb (D21 methodology) with comprehensive bp set (17 base + regex matches). Apparatus identical to D23 deployed state at probe entry; identical at probe exit (verified). Notes deliverable notes/d24-residual-gate-probe.md §A-§P. Pre-registered hypotheses W1/W2/W3 all falsified; actual mechanism named W4 per ADR-0006 (no plausible-fit forcing).
W4 — assertionq dispatcher is the common funnel; side-channel peer is the second input:
- D23 NOP-evict still effective — bp4 (the D23-NOP'd
removeCachedXPCConnection) NEVER fired during the trigger run. - XPCError 1001 IS BACK in unified log line 47 (t=14:34:57.171 in capture) — but on a SECOND XPC connection:
peer[2019]of a "side channel peer" connection that is distinct from both the action-XPC connection AND fromActionConnectionCache. Verbatim:[com.apple.dt.coredevice:useassertion] Recieved error from side channel peer: XPCError(errorCode: 1001, "The connection was invalidated.") - Mercury's
XPCSideChannel-installed event handler delivers this error to CD's[useassertion]subsystem. The subsystem posts a block tocom.apple.dt.coredevice.remotedevice.default.assertionq(the SAME queue D21 saw). - The assertionq dispatcher runs
___30eb0AGAIN, but at a DIFFERENT internal call site:- D21 (cache-eviction input):
___30eb0+2034→___324a0+98→swift_continuation_throwingResume - D24 (side-channel input):
___30eb0+1910→___324a0+56→swift_continuation_throwingResumeWithError - Same wrapper functions (frames 2 + 4 + 5+ identical), DIFFERENT internal RetPC offsets — proves the dispatcher chain is a SHARED FUNNEL with multiple input paths.
- D21 (cache-eviction input):
- errorCode 3 user-visible at unified.log line 50 (t=14:34:57.285):
Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3, "Failed to acquire assertion"). The literal stringFailed to acquire assertionlives in CoreDevice__cstringat file offset 0x369860 (notes/d24 §J.1).
W1/W2/W3 falsifications:
- W1 (post-success validation on reply payload) — no validation logic; action XPC reply decodes as
success()with no error key (notes/d24 §I.2). Construction happens AFTER reply-decode path completes. - W2 (separate cache or registry) — regex bps for
UsageAssertionCache,checkAssertion,validateAssertion,registerAssertionmatched ZERO symbols in CoreDevice. No second cache. - W3 (analytics-side gate) — analytics emit at unified.log lines 44-45 fires BETWEEN side-channel error (.171) and user-visible error (.285) — passive observer, not a gate.
D25 fix vector ranking (notes/d24 §N):
- Q-1a (PICKED): hook the funnel at
CD+0x31621(=___30eb0+1905, the call instruction whose return PC is the captured frame-3 RetPCCD+0x31626). NOP/wrap pattern, mirrors D23 P-1a. The dispatcher receives errors from BOTH cache-eviction AND side-channel inputs (D21+D24 evidence both routes through this dispatcher). Hooking the funnel addresses both inputs in one patch. - Q-1b (Mercury XPCSideChannel handler hook at Mercury+0x1cae0) — backup. Mercury binary not on disk (lives in dyld shared cache); broader scope (would affect non-AUA side-channel users).
- Q-1c ([useassertion] subscriber hook via os_log string xref at CD file offset 0x371320) — backup. Caller offset not yet derived; D26+ static disasm could resolve.
- Q-2 (CDS-side suppression) — last-resort. Requires CDS-side lldb attach + invasive lifecycle modification.
Implication for D25: Q-1a primary approach. Single hook expected to address both cache-eviction AND side-channel inputs to the assertionq dispatcher. If Q-1a deploys cleanly, D23 P-1a NOP-evict on removeCachedXPCConnection becomes redundant (funnel hook covers it) — revert/retain decision deferred to post-D25 acceptance test.
D24 also surfaced 7 D26+ open questions (notes/d24 §O): exact branch instruction at CD+0x324d3; errorCode 3 literal-load instruction site; string-xref caller for "Recieved error from side channel peer"; Mercury binary path on this build; CDS-side trace for side-channel teardown source; whether D23 NOP-evict becomes redundant if Q-1a deploys; how CD's [useassertion] subscribes to Mercury's XPCSideChannel events.
Q-D66-15 — D25+D26+D27+D28+D29 (2026-04-30, full chain): FORGE plan invalidated; 0/N AUA SUCCESS confirmed as natural baseline; cosmetic resolution accepted¶
Status: COSMETIC GAP ACCEPTED. FORGE DEFERRED INDEFINITELY pending daemon-side disasm.
Chain summary (research+writer cycle following D24's Q-1a recommendation):
-
D25 (research,
notes/d25-funnel-hook-research.md) — picked FORGE strategy J-c: hook AUA Swift async wrapper___317a0head (file offset0x317a0), fabricateCoreDevice.DeviceUsageAssertioninstance via type-metadata accessor at0xd3570, drive_swift_continuation_resumedirectly. Justified by §I empirical finding that awaiter has no fallback resume (___31750+___317a0disasm shows only_swift_task_switch/_swift_task_alloc/_swift_continuation_init/_swift_continuation_resume— nodispatch_after,Task.sleep,withTaskCancellationHandlerpatterns), so suppressing the funnel error without driving completion would orphan the continuation. NOP/wrap strategies A/B/C all UNVIABLE without forging a DUA. -
D26 (writer, halted at Step 3a
OFFSET_UNRESOLVED) — c-developer agent attempted to derive DUA field offsets via empirical disasm before writing the C forge handler, per ADR-0006 +feedback_correct_solutions_only. Halted on Stop Conditions 3 + 4 (notesnotes/d26-aua-forge-writer.md§B). Disasm of___324a0+0x40..+0x97and___317a0+0x7b..+0xf5(the two reference sites D25 §K specified) found NOswift_allocObjectcalls allocating a DUA;___324a0is an Error-rethrow / Result-coercion helper (itsleaq 0x3b17ce(%rip)targets are mangled-name strings for__swift_instantiateConcreteTypeFromMangledNameV2LOOKUP, not DUA field stores);___3bc80is also Error-coercion wrapper that tail-calls___324a0. The actual DUA construction site is INSIDE___304a0legacy completion-handler callback OR XPC decoder for AUA reply payload — never mapped in D21-D27. Additionally, the brief's assumed_swift_continuation_resume(continuation, value, metadata)call signature was wrong:___317a0's tail-jmp at+0xf5sets%rsi=0, %rdx=0, %rcx=0xD000000000000030, %r8=tagged-string-ptr, %r9=async-helper— Swift 5.7+ async-frame call convention, not the simple(cont,val,meta)value-resume D25 assumed. D25's structural premise that___324a0/___3bc80are DUA constructors was empirically FALSIFIED. -
D27 (research,
notes/d27-gambit-completeness-research.md) — disambiguated discriminator0x347e00at___317a0+0x72: it's a 6-byte chained-fixup stub for_$ss042_stdlib_isOSVersionAtLeastOrVariantVersiondE0yBi1_Bw_BwBwBwBwBwtF(libswiftCore — Swift'sif #available(macOS X.Y.Z, *)runtime helper). Reads only compile-time-baked args (rdi=0xf, rcx=0x12, rest=0) + running macOS version. The same stub is invoked from 150+ call sites across CoreDevice (everyif #availablecheck). NOT inject-flippable. Verdict (b) — FORGE plan unchanged from D25, but with bonus FORGE-anchor refinements: legacy completion-handler entry at0x304a0(symmetric to___31750); DUA type-metadata accessor at0xd3570. Both branches of theif #availablecheck route through the same Mercury+side-channel transport — there is no second transport for AUA. -
D28 (research,
notes/d28-true-branch-liveness.md) — runtime liveness probe:___317a0family (6 BPs at___31750,___317a0,+0x77,+0xf5,+0x599,___304a0) NEVER fires across 5 launch-under-lldb invocations of devicectl. All BPsnum_loc=1, resolved=1, hits=0; verify-byte at+0x77confirmsa8 01 74 7f(test al,1; je rel8 +0x7f). Run-1 logs show devicectl XPC-forwards the AUA action:[action] Forwarding action ... to ExecutionLocation.elsewhere(<SystemXPCPeerConnection ... CoreDeviceService, pid=4454 ...>). AUA work runs server-side in CoreDeviceService daemon, not client-side in devicectl. This contradicts D21 v2's interpretation but is reconcilable: D21 ran PRE-D23 keepalive when cache-eviction error path triggered client-side Error coercion that made___31750fire; post-D23 that path is short-circuited and___317a0family doesn't run client-side at all. D21's evidence was correct for its (pre-D23) apparatus state but became obsolete after D23 deployed. -
D29 (research,
notes/d29-aua-failure-pattern.md) — N=20 invocations to characterize D28's ⅘ SUCCESS rate (D28's intermittent observation). 0/20 SUCCESS, 100% deterministic FAILURE with Wilson 95% CI [83.9%, 100%]. All 20 produced baresuccess()reply (Codable Void Output, by GAMBIT design —gambit_build_void_box()for AUA per source line 1133) followed byFailed to acquire usage assertion~10 ms later. Side-channel sc1001 fired in 4/20 only — does NOT correlate with outcome (all 20 are FAILURE regardless). CDS daemon pid stable (4454, no respawn between D28 and D29; etime monotonic; RSS drift 60 KB over 123 sec — no pressure event). Failure is the natural state, not intermittent. -
Post-D29 verification cycles (read-only follow-up, 2026-04-30 18:00..19:50):
- Source review of
inject/iosmux_gambit.mconfirmed GAMBIT design: AUA Output isgambit_build_void_box()(empty 0-key dict) by design (source line 467-470 + 1133); AFM payload ismangledTypeName: "CoreDevice.AssertionFulfilledMessage"+value: {identifier: <action's deviceIdentifier as xpc_uuid>, updatedSnapshot: <DSS connected>}viagambit_emit_afm_via_endpoint(line 926); send-barrier deferred cancel (line 1021-1023) ensures AFM lands before connection cancel. Nothing in source produces a populated success Output for AUA — D28's "⅘ success" cannot have come from GAMBIT producing different bytes. - Kickstart test (
launchctl kickstart -k user/501/com.apple.CoreDevice.CoreDeviceService, daemon respawned 4454 → 13385 fresh process address space): N=5 cycle still 0/5 SUCCESS. Process-state-corruption hypothesis REJECTED. - VM-reboot test (full havoc VM reboot + apparatus rearm): N=10 cycle still 0/10 SUCCESS. Fresh kernel, fresh tunneld, fresh CDS, fresh inject load, manually-rearmed apparatus by dispatcher → same 100% failure rate. State-degradation hypothesis REJECTED.
Cumulative empirical baseline (post-VM-reboot): 0/10 SUCCESS at fresh apparatus state. D28's claimed ⅘ SUCCESS was an anomaly — most likely misclassification artifact (unified-log lookback window --last 30s or --last 1m picked up Successfully acquired usage assertion log lines from a prior session that pre-dated D28's run window, and the agent's classification logic counted them as current-run signal). Without baseline-line-count diff (which D29 introduced as inject-log-lines-before.txt), D28 had no way to tell apart pre-existing log entries from current-run output.
Architectural conclusion: Q-D66-15's main-path resolution stands per aua-side-channel-mechanism.md — D23 keepalive eliminates XPCError 1001 on the action-XPC connection's cache-eviction path (D24 W4 finding). The residual Failed to acquire assertion user-visible message remains COSMETIC per pair-button-and-cfnetwork.md §"Pair button gating" — does NOT block install / debug / app-launch pipelines that flow through the existing remote_service_create_connected_socket interpose chain (per ADR-0009 §Consequences). FORGE plan deferred indefinitely because the actual DUA construction site requires daemon-side disasm (not client-side, per D28) AND a daemon-side hook would need careful interaction with GAMBIT's existing reply synthesis (which already short-circuits the action-dispatch layer before the DUA-construction code path would run server-side).
Next-action recommendation: move to Phase D.6.6-impl Phase ⅔ work items per stage2.md. Q-D66-15 cosmetic gap accepted as documented project state. Re-open only if downstream Xcode IDE pipelines (install / debug attach / app-launch) empirically fail because of the cosmetic AUA failure — current evidence per ADR-0009 §Consequences predicts they do not.
Q-D66-15 — D30 (2026-04-30 evening, AFM peer keep-alive deploy): FAILURE — peer lifetime is not the gate; cosmetic-accept REVERSED 2026-05-01¶
Status: REOPENED. Empirical failure. D31 research probe pending.
User directive (2026-05-01): restore full UI functionality. The cosmetic-accept decision recorded in the prior D25..D29 Resolution log is REVERSED. Architectural goal is full UI functionality (Connected status, no error, real iOS version, real Capacity in Xcode Devices window).
D30 hypothesis (the test the cosmetic-accept never ran): explicit xpc_connection_cancel + xpc_release of the AFM peer connection in gambit_emit_afm_via_endpoint (the cancel-after-send pattern from commit 28d7d89) is what drives Apple's AUA Swift wrapper at CD+0x31750 to resolve _shadowUseAssertion = .invalidated(error) → continuation.failure(CoreDeviceError(errorCode: 3)) thrown. Per aua-side-channel-mechanism.md admonition "Why GAMBIT's AFM endpoint emit alone does not satisfy V2".
Fix candidate (deployed): 3-edit patch on inject/iosmux_gambit.m. Globals g_afm_retained_peers (NSMutableArray) + g_afm_retained_lock (NSLock) at top of TU. Replace cancel-after-send block in gambit_emit_afm_via_endpoint with [g_afm_retained_peers addObject:(__bridge id)ep_conn] under lock — peer kept alive for the entire CDS process lifetime. Init lock + array at top of gambit_install_hook. Build sha 6c9b07bad7ac383c8a66d4660a1e04e41411608ff65286cce0eb808afde89f49, +16 bytes vs D23 v2 baseline. Backup of D23 v2 at /home/op/backups/iosmux/d30-pre-deploy/iosmux_inject.dylib.d23v2 (sha 52df2cc6...4803). CDS respawned via killall CoreDeviceService (PID 749 → 1940), both hook install paths (AUA keepalive NOP + GAMBIT trampoline) succeeded rc=0.
Result: FAILURE. Same CoreDeviceError(errorCode: 3, "Failed to acquire assertion") ~9.9 ms after success() reply. Latency identical to D23-v2 baseline.
Citations from havoc unified.log (2026-04-30 capture):
21:29:51.998775+0700 devicectl[2001]: ... Received reply from action
(type=AcquireDeviceUsageAssertionActionDeclaration,
invocation=33377F43-...): success()
21:29:52.008620+0700 devicectl[2001]: [com.apple.dt.coredevice:useassertion]
Failed to acquire usage assertion on device
E8A190DD-... due to error: CoreDeviceError(errorCode: 3,
errorUserInfo: ["NSLocalizedDescription":
"Failed to acquire assertion"])
GAMBIT path executed correctly (peer retained, no cancel scheduled):
[inject] GAMBIT: synthesised reply for aid=com.apple.coredevice.action.acquireusageassertion
[inject] GAMBIT: emitted AFM endpoint=0x7fc754107cb0 did=E8A190DD-...
Critical absence: zero XPCError 1001 lines anywhere in the captured 60s window. Zero peer[2019] references. Zero Recieved error from side channel peer strings. D30 DID prevent peer invalidation via the cancel-after-send path — the cancel was the cause of the 1001 surfacing, but the 1001 surfacing was NOT the cause of errorCode 3. Peer lifetime is not the gate.
What D30 ruled out: AFM peer cancel-after-send is what drives .invalidated(error). RULED OUT. With cancel removed and peer held for entire CDS process lifetime, AUA outcome is byte-identical (same errorCode 3, same NSLocalizedDescription, same ~10 ms latency).
What D30 reveals: the dispatcher chain has a third uncatalogued input source (input #3) that produces Failed to acquire assertion even when (a) action XPC reply is success(), (b) cache-eviction is NOP'd (D23 P-1a), and © AFM peer never invalidates (D30). The ___30eb0/___3bc80/___324a0 dispatcher chain may still be the final funnel, but its input here is not the side-channel invalidation D24 documented.
Empirically falsified inputs (DO NOT retest):
- Byte-shape (D20): AFM payload mangledTypeName / shape / identifier
- Cache-eviction (D23):
ActionConnectionCache.removeCachedXPCConnectionat CD+0xdbe0 - Peer lifetime (D30): AFM peer connection cancel-after-send
Open hypothesis space (5 candidates for D31 research probe):
- AFM identifier value mismatch — GAMBIT sends action's
CoreDevice.deviceIdentifierUUID as assertion identifier in AFMvalue.identifierfield. Apple's AUA wrapper may match againstIdentifiableAssertionDetails.identifier(separate assertion-instance UUID generated wrapper-side, not present in request envelope). - Mercury.XPCSideChannel framing layer above raw xpc_connection — AFM endpoint we send on is raw libxpc (
xpc_endpoint_create/xpc_connection_create_from_endpoint). Apple's AUA wrapper may expect Mercury-framed message on top. - AFM timing race vs success-callback — GAMBIT emits AFM before returning synthetic
success()reply. AUA wrapper may subscribe to AFM only AFTER success callback fires onassertionq→ AFM dropped. - Separate notification channel — AFM-via-endpoint may not be the right delivery vehicle. Wrapper may listen on darwin notification (
notify_post/notify_register) or Mercury KVO property update or separate XPC service. - DSS field validation —
updatedSnapshot.monotonicIdentifierin AFM payload may be validated against snapshot cached at action-dispatch time; rejected if not strictly newer (mirrors Q-D66-14 RDSUE filter).
Apparatus integrity (D30 also non-destructive): iPhone (iosmux) connected (no DDI) unchanged. CoreDevice sha unchanged. Zero new diagnostic reports. Backup at /home/op/backups/iosmux/d30-pre-deploy/iosmux_inject.dylib.d23v2 intact and ready for revert.
Architectural conclusion: Q-D66-15 is REOPENED. The cosmetic gap framing of D29 — "does NOT block install/debug/app-launch pipelines" — was correct at the install-pipeline scope but does not satisfy the user-stated UI-functionality goal. UI shows "Disconnected" + "Failed to acquire assertion" because _shadowUseAssertion.fulfilled stays nil; making UI Connected requires AUA wrapper to resolve .fulfilled → input #3 must be localised and addressed.
Next-action: D31 research probe (general-purpose agent, RESEARCH not implementation per feedback_split_research_and_implementation). lldb dynamic attach to devicectl during AUA invocation in post-D23 + post-D30-revert apparatus. Goal: localise EXACT instruction site that synthesises residual errorCode 3. Breakpoints: swift_continuation_throwingResume* (full stack walk), "Failed to acquire" cstring xref sites at CD file offsets 0x371880/0x3718e0, CoreDeviceUtilities.CoreDeviceError.init(code:userInfo:) (D21 §K — 3 locations), CD+0x304a0 legacy completion-handler API (per D27: ___317a0 FALSE branch invokes; per D28: ___317a0 family runs server-side post-D23, but legacy callback path may run client-side). Apparatus must be reverted to D23 v2 BEFORE probe (D30 dylib reverted to baseline; D30 source edits reverted).
Distillation status: D30 raw notes (notes/d30-afm-peer-keepalive.md, ~32 KB, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + citations preserved in this entry + aua-side-channel-mechanism.md §"D30 update: AFM peer keep-alive — FAILED" + REOPEN admonition near top of that doc.
Q-D66-15 — D31 (2026-05-01, lldb dynamic probe v2): input #3 IS peer[1013] race; SUCCESS path empirically observed; D32 designs cause-side suppression¶
Status: REOPENED, race localised. D32 next.
D31 v2 dispatch (general-purpose research agent, lldb attach to client-side devicectl, three trigger runs against AUA invocation in post-D30-revert apparatus). Empirically localised the third input source named in the D30 admonition.
Construction site:
| Symbol | Module | File offset | Role |
|---|---|---|---|
CoreDeviceError.init(code:userInfo:) |
CoreDeviceUtilities | +0xbabf0 |
Final ctor — code:Int32 = 3 in rdi |
Swift.Error<...CoreDevice._Error>.init(Int32, String) |
CoreDeviceUtilities | +0xc10c6 |
Inner-call call-site |
Swift.Error<...CoreDevice._Error>.init(τ_0_0, Optional<String>) |
CoreDeviceUtilities | +0xc2780 |
Outer-wrap adds NSLocalizedDescription |
xpcError.getter |
CoreDeviceUtilities | +0xc2978 |
Bridge from XPCError to CoreDeviceError |
Caller chain (input #3 anchor inside CoreDevice):
| Symbol | File offset entry | PC at hit | Role |
|---|---|---|---|
___lldb_unnamed_symbol_30230 |
+0x30230 |
+0x30368 (inner) / +0x303cd (outer wrap) |
Input #3 synthesis caller |
___lldb_unnamed_symbol_31700 |
+0x31700 |
+0x31734 |
Outer caller — invokes ___30230 |
| Side-channel pump | CDU | +0x55510 / +0x55540 / +0x52510 / +0x52630 / +0x54060 / +0x56350 / +0x54ab0 |
Drain queue on _dispatch_lane_serial_drain |
Throw funnel: same ___30eb0/___3bc80/___324a0 dispatcher as D24 W4 input #2 (current build offsets +0x776 → +0x3d → +0x38). Inputs #2 and #3 share final dispatcher; differ only in WHICH XPC peer's invalidation drives the 1001.
THE PEER:
<SystemXPCPeerConnection 0x... { <connection: 0x... {
name = com.apple.xpc.anonymous.0x...peer[1013].0x...,
listener = false, pid = 1013, euid = 501, egid = 20,
asid = 100024 } }>
pid = 1013 is the CoreDeviceService daemon — i.e. the CDS↔devicectl primary side-channel connection. This is distinct from peer[2019] (the AFM peer that D30 retained). D30 left peer[1013] untouched, so its post-reply invalidation continued to fire and continued to be caught by the same [useassertion] observer that synthesises errorCode 3.
Race condition empirically confirmed (the architectural sea-change):
D31 captured TWO outcomes under identical apparatus state:
- Run #2 (success):
[useassertion] Successfully acquired usage assertion E8A190DD-...— the success-resume thread won the race. The 1001 STILL fires post-reply but arrives AFTER the success continuation resumed → throw becomes a no-op. - Run #3 (failure, iter 1 of 5-iteration retry):
Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3)— the side-channel observer drain pump won.
Two competing threads in devicectl post-reply:
- Thread A — success-resume path:
swift_continuation_throwingResume(no error variant). Triggered by GAMBIT-synthesisedsuccess(...)reply unwinding through CoreDevice's reply-handling code. - Thread B — side-channel observer drain pump: catches XPCError 1001 from peer[1013] invalidation, synthesises
CoreDeviceError(code:3)via the construction chain above.
Whichever thread first calls into the AUA continuation wins. Run #2 proves Thread A CAN win deterministically if the post-reply teardown path is short-circuited or delayed long enough.
Image base addresses (current build, CDS PID 1013, post-D30-revert apparatus):
| Image | __TEXT base |
|---|---|
| CoreDeviceService.xpc | 0x102b1d000 |
| CoreDevice.framework | 0x104c7b000 |
| CoreDeviceUtilities.framework | 0x103a99000 |
| CoreDeviceInternal.framework | 0x1032ad000 |
| iosmux_inject.dylib | 0x1032d4000 |
Process locality: client-side (devicectl). Both BP2 hits and the throw at BP1 fire inside the devicectl process. CDS daemon (PID 1013) reply is not the immediate trigger — the trigger is the post-reply peer invalidation propagated from CDS to devicectl via the side-channel.
Apparatus integrity (D31 also non-destructive): iosmux_inject.dylib sha unchanged (52df2cc6...4803); CoreDevice sha unchanged (bea205e2...0495); iPhone (iosmux) connected (no DDI); CDS PID stable at 1013 across all 3 lldb attaches; zero new diagnostic reports.
Strategy disposition for D32 (per dispatcher decision based on prior D23/D30 chained-NOP failure pattern):
- PICKED — suppress peer[1013] invalidation upstream of 1001 emission. Find what triggers peer[1013] invalidation CDS-side and block at AUA reply time. Closes race at cause, not symptom. User rationale: prior D23/D30 NOP attempts pulled chained downstream gates one-after-another; cause-side blocking is the only architecturally clean exit.
- REJECTED — NOP side-channel drain pump (CDU
+0x55510family). Risky blast radius — same pump may handle legitimate errors elsewhere. - REJECTED — NOP CD+0x30230 synthesis. Most localised but not AUA-specific. CD+0x303cd path also reached for non-AUA errors. Likely breaks other CoreDevice surfaces.
Next-action: D32 — research probe (general-purpose, lldb dynamic attach to CoreDeviceService daemon PID 1013) to localise the EXACT instruction site that triggers peer[1013] invalidation post-AUA-reply. Goal: identify a CDS-side hook point that prevents peer[1013] teardown during the AUA reply window. RESEARCH only, no implementation.
Distillation status: D31 raw notes (notes/d31-aua-errorcode3-localisation.md, ~575 lines, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + citations preserved in this entry + aua-side-channel-mechanism.md §"D31 update".
Q-D66-15 — D32 (2026-05-01, lldb attach to live CDS): peer[1013] invalidation is KERNEL-driven; no CDS-side hook point exists; D31 cause-side strategy empirically falsified¶
Status: REOPENED, kernel-driven trigger localised. D33 strategy decision pending (three options ranked).
D32 dispatch (general-purpose research agent, lldb running-attach to live CDS PID 1013, 12 BPs covering xpc_connection_cancel, _xpc_connection_cancel, xpc_remote_connection_cancel, _xpc_connection_dispose, Mercury XPCSideChannel.{deinit,__deallocating_deinit}, Mercury RemoteXPCConnection.{deinit,__deallocating_deinit}, RemoteXPCConnection.unsafePeer(from:forServiceNamed:), XPCSideChannel.send(message:), CoreDevice.XPCSideChannel.sendCancelledMessage(), and the CoreDeviceUtilities.invoke(anyOf:usingContentsOf:) action sentinel).
Two distinct XPC connections observed during AUA window:
| Connection | Address (run 5) | Identity | Cancel mechanism |
|---|---|---|---|
| A | 0x7f89c6e075e0 |
iosmux's AFM peer (xpc_connection_create_from_endpoint(input.endpoint) in GAMBIT) |
iosmux explicit cancel: __gambit_emit_afm_via_endpoint_block_invoke_2 → xpc_connection_cancel |
| B | 0x7f89c6a09040 |
peer[1013] — CDS↔devicectl primary side-channel anonymous accepted peer | Kernel-driven via MACH_NOTIFY_NO_SENDERS; ZERO application frames in chain |
Connection A lifetime is irrelevant to errorCode 3 (D30 already proved). Connection B is the input #3 source from D31.
Connection B's invalidation chain (entirely in libxpc, no app code):
Frame Function Module / offset
#00 _xpc_connection_cancel libxpc + 0x4d2f7
#01 do_mach_notify_no_senders +0x3c libxpc + 0x407c0
#02 _Xmach_notify_no_senders +0x21 libxpc + 0x40761
#03 notify_server +0x4e libxpc + 0x3ff32
#04 _xpc_connection_pass2mig +0x8e libxpc + 0x3fe77
#05 _xpc_connection_mach_event +0x4d5 libxpc + 0x39755
#06+ _dispatch_client_callout4 / dispatch / pthread bottom
Secondary cleanup chain at t+126ms confirms peer identity via _xpc_connection_remove_peer_impl +0x3c → _xpc_connection_remove_peer — internal libxpc code that removes an anonymous accepted peer from a listener's peer table. Matches D31's name = com.apple.xpc.anonymous.0x...peer[1013] shape.
Negative results: BPs ⅔/⅘/6/8/9/12 (Mercury/RemoteXPC/XPCSideChannel application-level) NEVER fired during AUA window. Mercury user-facing API was NOT exercised CDS-side. Connection B is owned directly by an XPC listener (likely the CDS service's own listener), bypassing all Mercury/CD/CDU layers.
The architectural sea-change for D33:
MACH_NOTIFY_NO_SENDERS fires when the OTHER side (devicectl) drops its last mach send right on the connection's underlying mach port. The trigger lives in devicectl's process, not in CDS. When devicectl finishes processing the AUA reply and releases its handle to peer[1013], the kernel delivers the notification to CDS, libxpc tears down its peer table entry, devicectl-side observes its own connection handle's invalidation and emits XPCError 1001 → input #3 fires.
peer[1013] cannot be kept alive CDS-side without devicectl also keeping its end alive. devicectl's release is governed by its own process-internal logic — no CDS-side hook can reach it.
D31's preferred strategy "suppress upstream of cause" remains correct in principle, but the empirical localisation now shows that "upstream" lives in devicectl, not in CDS. The CDS-side path that D31 picked for D32 is empirically impossible.
Three remaining strategy options for D33:
-
devicectl-side inject — new dylib loaded into devicectl via DYLD_INSERT_LIBRARIES (or
LC_LOAD_DYLIBon a patched devicectl copy following the existing CDS pattern in iter-18 findings). Hook the function inside devicectl that releases peer[1013]'s handle; defer the release past AUA continuation resolution. Most stable architectural exit but expands inject footprint to a new process. devicectl may have hardened runtime / library validation — codesign accommodation needed. Pre-existing project disposition: previously rejected as "REJECTED — overkill, 12-20 hours, new injection mechanism" inaua-side-channel-mechanism.mdInject-side fix vectors §P-1b. D32 finding reopens that rejection. -
Surgical hook on side-channel observer drain pump in devicectl — narrower than D31's "NOP drain pump" rejection. Hook
xpcError.getter(CDU+0xc2978) or one of the CDU+0x55510 family entry points; filter ONLY when error code is 1001 AND caller is in AUA context (stack walk discriminator). Bounded blast radius compared to blanket-NOPing the pump. Still requires devicectl-side inject. -
NOP CD+0x30230 synthesis in devicectl — least AUA-specific; CD+0x303cd path also reached for non-AUA errors. Still requires devicectl-side inject.
All three options require devicectl-side inject mechanism — no CDS-side option remains.
Apparatus integrity (D32 also non-destructive): iosmux_inject.dylib sha unchanged (52df2cc6...4803), CoreDevice sha unchanged (bea205e2...0495), iPhone (iosmux) connected (no DDI), CDS PID stable at 1013 across lldb attach/detach (running-attach worked first try), zero new diagnostic reports.
Distillation status: D32 raw notes (notes/d32-peer1013-invalidation-source.md, ~349 lines, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + libxpc invalidation chain + Connection A/B distinction + strategy disposition preserved in this entry + aua-side-channel-mechanism.md §"D32 update".
Q-D66-15 — D33 (2026-05-01, lldb attach to launching devicectl): DYLD_INSERT_LIBRARIES feasible; Option 1 reframed as symptom-suppression; Option 2 recommended for D34¶
Status: REOPENED, D34 implementation brief design pending dispatcher decision.
D33 dispatch (general-purpose research agent) closed three concrete questions before D34 implementation. The dispatcher's pre-dispatch preference for Option 1 needs reconsideration based on D33's empirical reframing.
Q1 — DYLD_INSERT_LIBRARIES injection: FEASIBLE¶
devicectl path: /Library/Developer/PrivateFrameworks/CoreDevice.framework/Versions/A/Resources/bin/devicectl, sha256 4fede2dd4fdbe542547b535f70ad320f8c33093cd23ff5b226aaad7c327cbf6c, Mach-O universal [x86_64 + arm64e], signed by Apple com.apple.CoreDevice.devicectl TeamIdentifier 59GAB85EFG.
Codesign properties:
- Library validation flag (
0x2000) PRESENT. - Hardened runtime flag NOT present (
runtimeflag absent — this is the key permissive condition). - Three Apple-private entitlements (none restrict dyld inserts).
- NO
com.apple.security.cs.allow-dyld-environment-variables(not needed when hardened runtime is off). - NO
com.apple.security.cs.disable-library-validation.
Empirical test: ad-hoc-signed no-op probe dylib loaded into devicectl via DYLD_INSERT_LIBRARIES=/tmp/iosmux-d33-probe.dylib. Probe constructor banner appeared in stderr ([d33] DYLD_INSERT_LIBRARIES probe loaded pid=3533); devicectl exited 0; no rejection messages.
Verdict: D34 does NOT need to design insert_dylib LC_LOAD_DYLIB patching of an Apple binary copy. Standard DYLD_INSERT_LIBRARIES injection works directly.
Q2 — Option 1 hook target REFRAMED — peer release is libxpc-internal, not app code¶
P3 lldb capture (xpc_release BP filtered for connections matching xpc_connection_get_pid == CDS-PID) localised the release call to:
#0 xpc_release+0x0
#1 libxpc.dylib`_xpc_connection_mach_event+0x418
#2 libdispatch.dylib`_dispatch_client_callout4+0x7
#3 libdispatch.dylib`_dispatch_mach_cancel_invoke+0x40
#4 libdispatch.dylib`_dispatch_mach_invoke+0x399
#5 libdispatch.dylib`_dispatch_root_queue_drain_deferred_wlh+0x113
#6 libdispatch.dylib`_dispatch_workloop_worker_thread+0x367
ZERO frames in CD/CDU/Mercury Swift app code. The release is a passive libxpc Mach-event reaction — the symmetric image of D32's CDS-side kernel-driven invalidation (CDS gets MACH_NOTIFY_NO_SENDERS from devicectl side; devicectl gets cancel-Mach-event from CDS side).
Two peer connections per AUA invocation (new finding):
0x7faad11056d0— first peer, AUA reply path0x7faad11043c0— second peer, side-channel "result peer" namedpeer[1013](the cancel recipient)
The actionable Option 1 hook target identified: Mercury.SystemXPCPeerConnection.__deallocating_deinit at Mercury+0x4ea10 (sister .deinit at +0x4e9d0). Mercury __TEXT base in P3 run: 0x100453000 (slide differs per launch).
BUT this is a Swift type's deinit chain that fires AFTER libxpc has already released the underlying mach-port-backed connection. Option 1 cannot achieve "prevent peer release" as the dispatcher originally hoped — the release is libxpc-internal. Option 1 can only modify post-release Swift cleanup, which is symptomatic, not causal.
Q3 — Option 2 hook target viability: CONFIRMED¶
CDU __TEXT base in current devicectl run: 0x1020a4000. All D31 §E offsets resolve to expected symbols:
| File offset | Resolved symbol | Notes |
|---|---|---|
CDU+0xbabf0 |
CoreDeviceUtilities.CoreDeviceError.init(code:userInfo:) |
NAMED Swift symbol — direct dlsym target |
CDU+0xc2940 |
static Swift.Error<...>.xpcError.getter (entry) |
NAMED Swift symbol |
CDU+0xc2978 |
static Swift.Error<...>.xpcError.getter +0x38 |
(same function) |
CDU+0x55510, +0x54ab0 |
drain pump unnamed symbols | Reachable via slide arithmetic |
Mercury+0x57b40 |
Mercury.XPCError.xpcError.getter |
NAMED Swift symbol — wire-level XPCError layer |
Mercury+0x5cd90 |
Mercury generic xpcError getter variant | NAMED |
dlsym/Apple-Swift-mangled-name lookup is sufficient for named targets. Same proven pattern as the existing CDS-side inject's hooks.
Bonus finding: CoreDevice.ActionConnectionCache.removeCachedXPCConnection at CD+0xdbe0 (the D23 NOP-evict target) is ALSO present in devicectl-side CoreDevice. Same class type with same method on both sides of the boundary; CDS-side instance is currently NOP'd, devicectl-side instance runs unmodified. Possible follow-up but out of scope for D33.
Strategy comparison for D34¶
| Dimension | Option 1 (Mercury deinit) | Option 2 (CDU error path) |
|---|---|---|
| Symptom vs cause | Symptom-side (Swift cleanup AFTER libxpc release) | Symptom-side (error construction) |
| Hook point named | Yes — Mercury.SystemXPCPeerConnection.__deallocating_deinit |
Yes — CoreDeviceError.init, xpcError.getter |
| Discrimination needed | Yes — peer's xpc_pid + AUA context | Yes — errorCode == 1001 + AUA-context-on-stack |
| Side-effects | Memory-leak risk (preventing deinit chain leaves connection-bookkeeping graph in CD/Mercury caches) | Lower — error construction is stateless; suppression doesn't keep dead state alive |
| Failure mode | Hook misses → connection still released (deinit was already too late anyway) | Hook misses → error surfaces normally |
| Implementation complexity | Higher — Swift deinit + refcount semantics on Mercury internal type | Lower — Swift function prologue rewrite |
Recommendation for D34: Option 2¶
Agent recommendation: Option 2. Rationale:
-
Both options are symptom-suppression — Option 1's "cause-side" framing was empirically falsified (release is libxpc-internal, not app code; Mercury deinit fires AFTER libxpc release). Given symmetric symptom-side scope, simpler symbol set wins.
-
Named Swift symbols dlsym-resolvable —
CoreDeviceError.init(code:userInfo:)andxpcError.getterare NOT stripped. Same proven pattern as CDS-sidegambit_install_hookfrominject/iosmux_gambit.m. -
No refcount/deinit hazards — Option 1 prevents a Swift type's deinit chain; Mercury's internal connection-bookkeeping graph may rely on the deinit cascade, and preventing it could leak Mercury state. Option 2 hooks stateless error-construction functions — no state-machine entanglement.
-
Filter discrimination is bounded — Option 2 filter:
code == 1001AND caller-stack contains AUA path symbols. Option 1 filter: peer'sxpc_connection_get_pid == CDS-PIDAND deinit context is AUA-related. Both feasible but Option 2's discriminator is on values (errorCode integer) not on object pointers (xpc_pid).
Apparatus integrity (D33 also non-destructive)¶
- iosmux_inject.dylib sha unchanged (
52df2cc6...4803). - CoreDevice sha unchanged (
bea205e2...0495). - devicectl sha unchanged (
4fede2dd...bf6c) — Apple binary untouched throughout the probe. - iPhone (iosmux)
connected (no DDI). - CDS PID stable at 1013 across all probe steps.
- Zero new diagnostic reports.
- Test probe dylib at
/tmp/iosmux-d33-probe.dylibon havoc — will not survive reboot.
Distillation status¶
D33 raw notes (notes/d33-devicectl-feasibility.md, ~562 lines, gitignored) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + injection feasibility verdict + Option 1 reframing + Option 2 verification + strategy comparison preserved in this entry + aua-side-channel-mechanism.md §"D33 update".
See also¶
d66-research-questions.mdd66-history-q15-d34-d40.md— Q-D66-15 D34..D40 continues from this archive.d66-history-q1-q14.md— earlier Q-D66 questions Resolution logs.../research/coredevice-internals/aua-history-d24-d35.md— architectural distillation of D24..D35a era.