Q-D66-15 history — D34..D40 (2026-05-01..2026-05-02)¶
Status: verified — historical archive
Q-D66-15 Resolution log entries for D34/D35a (Heisenbug),
D36 (host-side static disasm), D37-C/D/E/F/G (focused probes),
D37-H (post-VM-reboot baseline), D38 G-4/G-1/G-1' (cause-side
falsifications), empirical conclusion across D38, D39 (Xcode-side
H#2 deploy + integrity incident + recovery), D40 (state-aware
retrospective). Archived from
d66-research-questions.md on
2026-05-02 anchor refactor.
Q-D66-15 — D34+D35a (2026-05-01, lldb attach to launching devicectl): Swift Deinit chain is LATE cleanup; HIT #18 trigger UNRESOLVED; Heisenbug found¶
Status: REOPENED, cause-side trigger UNRESOLVED. D36 method-pivot pending (lldb-based observation perturbs the path).
D34 (lldb attach + 12 BPs on connection lifecycle) localised peer[CoreDevice.DeviceUsageAssertion.deinit → ... → Mercury.SystemXPCPeerConnection.__deallocating_deinit → xpc_connection_cancel chain (HIT #23 at t=111.269) fires 3.7 seconds AFTER an earlier libxpc-internal cancel (HIT #18 at _xpc_connection_mach_event+0x310, t=107.510). H1 (Swift type holds peer) is structurally true but hooking DeviceUsageAssertion.deinit doesn't prevent HIT #18 — Swift chain is late lateral cleanup, NOT the trigger.
D35a (follow-up lldb probe targeting _xpc_connection_mach_event ENTRY with dispatch_mach_reason_t register dump in $rsi) discovered a Heisenbug: lldb perturbation altered the AUA failure path itself.
Two empirical AUA failure paths identified:
| Path-A (D34 reference, no lldb on devicectl) | Path-B (D35a under lldb instrumentation) | |
|---|---|---|
| Main XPC reply | success() | success() |
Successfully acquired log line |
YES | NO (analytics shows executionDuration=0) |
Recieved error from side channel peer: 1001 log |
YES (peer[1013].0x...) | NO — log line absent |
| Final outcome | Failed to acquire ... errorCode 3 |
Failed to acquire ... errorCode 3 (same) |
| Trigger pattern | success → side-channel cancel → 1001 → errorCode 3 | success → errorCode 3 directly, NO side-channel error |
Under lldb BP-based instrumentation, the side-channel peer connection was either never created or created in a way that xpc_connection_get_pid filtering missed (17 _xpc_connection_mach_event hits in D35a, 0 matched CDS-PID). The brief's deliverable (decode dispatch_mach_reason_t at the trigger event) couldn't be answered because path-A didn't reproduce.
Verdict on H1/H2/H3/H4: NONE empirically confirmed. All four hypotheses for HIT #18 trigger remain open. lldb-based observation cannot reach this trigger — instrumentation perturbs timing enough to suppress path-A reliably.
NEW finding worth flagging: errorCode 3 has at least TWO production code paths in current apparatus (path-A with side-channel peer error, path-B without). Any GAMBIT-defeat strategy may need to address both, not just the side-channel peer error.
Recommendation for D36: switch observation method:
-
dtrace pid provider on devicectl —
pid$target:libxpc:_xpc_connection_mach_event:entrywith arg0/arg1 dump to ring buffer. Lower per-probe overhead than lldb BPs (no SIGSTOP/SIGCONT cycle, no Python callback eval) — should preserve path-A timing. -
CDS-side inject capture — extend GAMBIT logging in
iosmux_inject.dylibto log every mach msg emitted viaxpc_connection_create_from_endpointpeer (destination port name, msgh_id, msgh_bits). Sidesteps devicectl-side observation entirely. -
os_logpredicate stream withsubsystem:com.apple.coredevice category:useassertionat debug level — captures internal trace points that don't print at default level. May reveal whether side-channel peer was created in path-A vs not in path-B without lldb in the loop.
Strongest combination: (1) dtrace + (3) os_log concurrent, no lldb. dtrace's lower overhead preserves path-A timing; os_log gives userspace-level observability without affecting trace.
Apparatus integrity (D34 + D35a both non-destructive): inject sha unchanged (52df2cc6...4803); CoreDevice sha unchanged (bea205e2...0495); devicectl sha unchanged (4fede2dd...bf6c); iPhone (iosmux) connected (no DDI); CDS PID stable at 1013; zero new diagnostic reports.
Distillation status: D34 stub notes (~2.9 KB, §A only — rest analyzed inline in dispatcher chat) and D35a notes (notes/d35a-libxpc-cancel-trigger.md, ~17 KB, §A-§H populated, §I PENDING) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + path-A/path-B distinction + Heisenbug observation + D36 method recommendations preserved in this entry + aua-side-channel-mechanism.md §"D34+D35a updates".
Q-D66-15 — D36 (2026-05-01, host-side static disasm): Path-A/Path-B converge on DevicectlExecutor._completionGuard semaphore; XPCError→CoreDeviceError(3) hardcoded at xpcError.getter; 5 hook candidates ranked¶
D36 chose Option 4 (host-side static disasm) over the 3 lldb/dtrace alternatives because of D35a Heisenbug. Four Apple binaries pulled READ-ONLY from havoc to /home/op/dev/iosmux/binaries/, analysed with ghidra-analyzeHeadless, llvm-objdump, swift-demangle, radare2. Apparatus integrity: zero modifications to havoc binaries; iPhone (iosmux) connected (no DDI); CDS PID 1013 stable.
Three unnamed devicectl symbols IDENTIFIED (D34/D35a target offsets):
0x100016970=CoreDeviceCLISupport.AnyDevicectlCommand-extension method (CLI result printer)0x100092c60= command error finalizer (buildsEncodableCommandOutput, callsExitCode.exit())0x100095660=devicectl.DevicectlExecutor.signalCompletion— doesdispatch_semaphore_wait(_completionGuard)thendispatch_source_cancel(_timerSource)
Path-A and Path-B converge: both terminate at signalCompletion (FUN_100095660). 5 callers of this finalizer; static cannot classify which is Path-A vs Path-B without runtime probe.
Empirical chain confirmed end-to-end (Path-A):
Mercury.XPCError(1001, peer[1013])
→ Mercury.XPCError.normalized(as: CoreDeviceError.Type) (Mercury+0x52d50)
→ static (extension in CoreDevice):Swift.Error.xpcError.getter (CDU+0xc2940)
→ CoreDeviceError._init(code: 3, userInfo: ...) (CDU+0xbabf0)
→ _Continuation.resume(throwing:) (CDU+0x5fb70)
→ ActionConnection trampoline `(*[r13+0x10])(0)` (CDU+0x55510)
→ DevicectlExecutor.signalCompletion (devicectl+0x95660)
→ dispatch_semaphore_wait(_completionGuard)
→ dispatch_source_cancel(_timerSource)
The mov edi, 3 at CDU+0xc296b proves the XPCError → CoreDeviceError conversion is hardcoded at xpcError.getter — it ALWAYS returns CoreDeviceError(code: 3) regardless of original XPCError code. The 1001 only survives in userInfo.NSLocalizedDescription.
_timerSource cancel — D35a "dispatch_channel_cancel" was lldb symbol-aliasing: Ghidra resolves the call at devicectl+0x100095660+0x70 directly to OS_dispatch_source::cancel(_timerSource) (the executor's deadline timer), not dispatch_channel_cancel. lldb's symbol-name resolution may have aliased the libdispatch internal helper.
_Continuation.resume(throwing:) does NOT enforce resume-once visibly: CDU+0x5fb70 decompile shows no atomic, no flag, no early-return — just unconditionally builds Result.failure(...) and calls vtable. Resume-once protection comes from EITHER (a) Swift Concurrency runtime continuation slot atomic in libswift_Concurrency.dylib, OR (b) _completionGuard semaphore acting as mutex above the continuation layer.
CDU+0x55510 is a 35-byte trampoline, not a "drain pump". 3 callers, all in $forward (the ActionDeclaration.forward<Input == ()>(...) extension at CDU+0x54c90). Compiler-emitted Swift async closure trampoline. D31's "drain pump" framing was runtime-attribution from _dispatch_lane_serial_drain stack frames; statically the function is part of the action-forwarding mechanism.
Hook candidates (NO implementation; static analysis only) ranked by confidence + cleanness:
_Continuation.resume(throwing:)at CDU+0x5fb70 — NAMED Swift symbol, dlsym-resolvable. Choke point for ALL action errors. Hook would inspect error in$rsi, filter by errorCode==3 + AUA context, redirect to_Continuation.resume(returning:)at CDU+0x60160. Open: AUA's Output isVoid, what value to pass.Swift.Error<...>.xpcError.getterat CDU+0xc2940 — NAMED, called from Mercury. Hook would short-circuit when underlying XPCError code ∈ {1001} AND caller is AUA-related. Higher complexity (must fabricate alternative return value of generic typeA: _Error).CoreDeviceError._init(code:userInfo:)at CDU+0xbabf0 — NAMED Swift initializer. Hook filterscode == 3AND AUA stack-context, rewrites tocode == 0or skips construction. Concern: error is constructed at this point but resume-throwing not yet called.DevicectlExecutor.signalCompletionat devicectl+0x95660 — convergence point. Hook would gate which path proceeds. Static cannot classify the 5 callers as Path-A vs Path-B without runtime probe; hook utility unclear.DevicectlExecutor._completionGuardsemaphore (pre-signal from inject) — would require Swift class metadata walk to find field offset withinDevicectlExecutor, thendispatch_semaphore_signalfrom inject-side. Allows pre-allowing one path; doesn't prevent the other from also callingsignalCompletionlater.
All 5 candidates are in CoreDeviceUtilities or devicectl; both load into the devicectl process. D33 already proved DYLD_INSERT_LIBRARIES injection feasible.
What static analysis cannot resolve (requires runtime data):
- Which of FUN_100095660's 5 callers maps to Path-A vs Path-B — needs dtrace
pid$target:devicectl::FUN_100095660:entrywith ustack. - Field offset of
_completionGuardwithinDevicectlExecutor(Swift field-descriptor table walk needed at runtime). - Whether resume-once is enforced AT
_Continuationlevel via external state flag, or only at libswift_Concurrency.dylib level. - Q2 (peer release trigger): static adds NO new data. D32 verdict stands — kernel-driven via
MACH_NOTIFY_NO_SENDERS. Mercury+0x4e9d0SystemXPCPeerConnection.deinitexists but fires AFTER libxpc release.
Apparatus integrity: all four havoc binaries unchanged (READ-ONLY pulls only): devicectl 4fede2dd…bf6c, CoreDevice bea205e2…0495, CoreDeviceUtilities b7ce01c4…7204, Mercury 753f919e…cdbb. iosmux_inject.dylib sha unchanged (52df2cc6…4803). iPhone (iosmux) connected (no DDI). CDS PID 1013 stable. Zero new diagnostic reports.
Distillation status: D36 raw notes (notes/d36-host-disasm.md, ~698 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Per-question text outputs preserved at /home/op/dev/iosmux/symbols/ (host-side, not in repo). Ghidra project at /home/op/dev/iosmux/ghidra/D36/ re-runnable. Empirical findings + chain map + 5 hook candidates preserved in this entry + aua-side-channel-mechanism.md §"D36 update".
Q-D66-15 — D37-C (2026-05-01, focused ABI probe): hook #1 mechanically feasible for Void Output but semantically masking a denial; hooks #1/#2/#3 all deprecated; viable candidates narrow to #4/#5 (race-timing) or upstream peer-release prevention¶
D37-C single-question Ghidra probe (run on the existing D36 project at /home/op/dev/iosmux/ghidra/D36) determined the Swift ABI for _Continuation.resume(returning:) at CDU+0x60160 to assess viability of D36 hook candidate #1 (redirect Path-B's resume(throwing:) → resume(returning:) with Void success).
ABI findings:
resume(returning: A)at CDU+0x60160: RSI = self, R13 = ptr to indirect A-buffer, A's VWT read fromself+0x10, dynamic stack via__chkstk_darwin. Body uses VWT slot 0x10 (initializeWithCopy) to copy A into Result-shaped local, sets enum tag=0 (.success), calls shared tailFUN_00060680.resume(throwing: Error)at CDU+0x5fb70: DIFFERENT shape — RDI = error existential, no A-buffer, tag=1 (.failure), adds_swift_errorRetain. Same shared tail. NOT trivially interchangeable.- ZERO direct callers of
resume(returning:)inside CoreDeviceUtilities. Void-Outputforward<>specializations use(Error?) -> ()trampolines (FUN_00055510 =(*(R13+0x10))(0)) that bypassresume(returning:)entirely. Real callers live in CDS / CoreDevice / Mercury (where the AUA action's witness table is held).
Hook #1 mechanical feasibility: HIGH (~10 lines of asm). For Void Output specifically, no value fabrication needed (size 0, () has no-op VWT), self pointer in RSI is reusable from the throwing-caller frame, R13 buffer pointer stays valid. Trampoline: filter by error type + caller context, _swift_errorRelease(rdi), xor edi, edi, jmp resume_returning_at_0x60160_after_chkstk_prologue.
Hook #1 semantic verdict: UNSOUND. AUA's errorCode==3 from the side-channel chain is CDS's authoritative denial — synthesizing success at the continuation level would make devicectl's awaiter proceed as if authorization succeeded, then fail unpredictably at the next async step (no real session, peer invalidation handled with stale state, no XPC payload). The hook is a syntactic forgery that contradicts the action's semantic contract.
Implication for D36's 5 hook candidates:
- #1 (
resume(throwing:)redirect): DEPRECATED by this verdict. - #2 (
xpcError.getterfilter at CDU+0xc2940): same semantic flaw — masks the denial signal at a different layer. DEPRECATED by analogy. - #3 (
CoreDeviceError.initfilter at CDU+0xbabf0): same semantic flaw — masks the denial. DEPRECATED by analogy. - #4 (
signalCompletiongating at devicectl+0x95660): different layer — affects which path WINS the race rather than masking the loser. STILL VIABLE. Static cannot classify which of the 5 callers is Path-A vs Path-B; runtime probe (dtrace) needed before implementation. - #5 (pre-signal
_completionGuardfrom inject): race-timing approach. STILL VIABLE but requires Swift class-metadata field-offset walk at runtime. Doesn't prevent Path-B from also callingsignalCompletionlater.
Viable directions narrowed to: race-timing (candidates #4 / #5 — make Path-A's actual success win the race deterministically) OR upstream prevention of peer[1013] release in devicectl (currently un-localised; D34's Swift DeviceUsageAssertion.deinit chain at HIT #23 fires 3.7s AFTER HIT #18 libxpc cancel, so HIT #18's trigger remains upstream of any code site we have mapped).
Apparatus integrity: all four havoc binaries unchanged (READ-ONLY analysis only); same checksums as D36; iPhone (iosmux) connected (no DDI); CDS PID 1013 stable; zero new diagnostic reports.
Distillation status: D37-C raw notes (notes/d37c-continuation-abi.md, ~516 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Decompile dumps preserved at /home/op/dev/iosmux/symbols/c-continuation-resume-abi.txt (~3079 lines, host-side, not in repo). Verdict + ABI spec + candidate ranking update preserved in this entry + aua-side-channel-mechanism.md §"D37-C update".
Q-D66-15 — D37-D (2026-05-02, host-side static disasm continuation): HIT #18 trigger LOCALISED to Mercury.SystemXPCListenerConnection.deinit at Mercury+0x4e930; cause-side hook target identified; needs dtrace empirical confirmation¶
D37-D continued the D36 host-side disasm to find the upstream code site that drops the LAST strong reference to peer[1013] BEFORE Swift DeviceUsageAssertion.deinit chain at HIT #23 (which fires 3.7s after HIT #18 at t=107.510s — therefore LATE cleanup, not the trigger). Apparatus integrity verified non-destructive.
Three holders of peer[1013] in devicectl (Q-D-1):
Mercury.SystemXPCListenerConnection(anonymous listener at CD+0x304a0+0x152) — holds accepted peer transitively via libxpc peer-tablelVar17AUA-completion context (heap object at CD+0x304a0+0x186) — captures listener at +0x18, nested closure at +0x28CoreDevice.DeviceUsageAssertion._IndexBox<peer>— boxed peer field, releases at HIT #23 (LATE)
Trigger localised (Q-D-3): Mercury.SystemXPCListenerConnection.deinit at Mercury+0x4e930. Body does _swift_unknownObjectRelease(listener_xpc) which drops devicectl's last strong ref to the listener OS_xpc_connection. libxpc internally walks the listener peer-table and propagates cancellation to each accepted peer — D32 observed this via _xpc_connection_remove_peer_impl at t+126ms. This IS what fires HIT #18 (D34's _xpc_connection_mach_event+0x310 with ZERO Swift app frames is consistent with libxpc internal listener-tear-down).
Static cannot pinpoint EXACT instruction (Q-D-4 / §E gap): the lVar17 AUA-completion context release is driven by Swift Concurrency runtime (libswift_Concurrency.dylib task-allocator destruction) — there is NO explicit xpc_connection_cancel(listener) in CoreDevice/CoreDeviceUtilities for AUA. Release is purely ARC. Per ADR-0006 we don't speculate about runtime internals.
Hook target (Q-D-5 / §D-1): prologue-patch Mercury.SystemXPCListenerConnection.deinit at Mercury+0x4e930 in devicectl via DYLD_INSERT_LIBRARIES (D33 P3 confirmed feasible).
- Stack-walk filter: any return PC in
[CD+0x30000, CD+0x32000)(AUA async wrapper family: 0x304a0/0x31750/0x31c00/0x317a0/0x31a50/0x31ab0) → AUA-context, suppress release. - Skip the
_swift_unknownObjectRelease(listener_xpc)call inside deinit. Optionallyxpc_retain(listener_xpc)upfront to keep libxpc bookkeeping consistent. - For non-matching listeners: tail-call original deinit prologue.
Confidence: MEDIUM-HIGH static. Listener-deinit-IS-trigger claim is consistent with D34/D32 evidence but not 100% verified — alternative is direct xpc_connection_cancel(listener_xpc) somewhere that static missed. Cross-binary xrefs are invisible (each Ghidra program standalone).
Side effects: anonymous listener + underlying OS_xpc_connection leaks per AUA invocation, bounded to devicectl process lifetime (short-lived CLI). CDS-side connection stays alive until CDS scope releases naturally (NO_SENDERS still fires EVENTUALLY but AFTER Path-A success-resume completes — Path-A wins deterministically).
Anti-recommendations (do NOT hook): Mercury+0x4e9d0 (peer deinit, HIT #23 LATE), CD+0xd3060 (DUA deinit, HIT #23 LATE), Mercury+0x1c450 (XPCSideChannel deinit, wrong type), CDU+0x5fb70 / CDU+0xc2940 / CDU+0xbabf0 (D37-C deprecated as denial-masking).
Recommended empirical follow-up (separate brief): dtrace pid-provider on Mercury+0x4e930 + libxpc:_xpc_connection_mach_event correlated by timestamp + ustack(20). If Mercury+0x4e930 fires at ~t=107.5s just before HIT #18 → §D-1 confirmed. If not → §D-1 falsified, alternative trigger to investigate. dtrace's lower per-probe overhead vs lldb avoids the D35a Heisenbug.
Apparatus integrity: all four havoc binaries unchanged (same checksums as D36/D37-C); iPhone (iosmux) connected (no DDI); CDS PID 1013 stable; zero new diagnostic reports.
Distillation status: D37-D raw notes (notes/d37d-upstream-peer-release.md, ~530 lines) deleted at distillation time. Per-binary ownership-trace dumps preserved at /home/op/dev/iosmux/symbols/d-ownership-trace-*.txt (host-side, not in repo). Trigger localisation + hook target + confidence + dtrace recommendation preserved in this entry + aua-side-channel-mechanism.md §"D37-D update".
Q-D66-15 — D37-E (2026-05-02, dtrace empirical confirmation): D37-D listener-deinit-IS-trigger hypothesis EMPIRICALLY FALSIFIED with strong Heisenbug caveat; symbol mapping corrected; alternative trigger candidates surfaced¶
D37-E ran dtrace pid-provider on havoc during AUA invocation to test §D-1. Apparatus integrity verified non-destructive.
Result: ZERO occurrences of Mercury+0x4e930 (listener.deinit) or any of 0x4e960/0x4e9d0/0x4ea10 deinit symbols across 4 runs × ~30k stack frames (≈120k+ frames total) at any of: 22 _xpc_connection_mach_event entries (HIT #18 with reason=0x2, ZERO Swift frames matches D34 reference), 1770 xpc_release entries, 2 xpc_connection_cancel, 4 _xpc_connection_cancel. The hypothesised Mercury Swift deinit chain did NOT fire on any captured cancel-or-release path.
Strong caveat: dtrace introduced Heisenbug similar to lldb (D35a). 6/6 runs failed to reproduce FULL Path-A — "Successfully acquired" log absent in all dtrace runs (was present in pre-dtrace baseline 08:56:15). 3/6 hit "Path-A-without-success-log" (side-channel peer error fires). It REMAINS POSSIBLE listener-deinit fires only on full Path-A and dtrace overhead alters ARC release timing.
Symbol mapping correction vs D37-D static brief (per llvm-nm empirical):
0x4e930= listener deinit body — UNCHANGED0x4e960= listener __deallocating_deinit (D37-D wrongly said 0x4ea10)0x4e9d0= peer deinit body — UNCHANGED0x4ea10= peer __deallocating_deinit (D37-D mismatched assignment)
Trigger candidate 0x4e930 listener.deinit is unchanged. Mismatched offsets do not affect §D-1 logic.
Methodological findings (constrain all future dtrace work):
- Apple dtrace v1.19 cannot probe Swift mangled symbols directly (
$parsed as macro variable). No documented escape. - Pid-provider symbol enumerator filters out
$-prefixed symbols even from:::entryglob. - AMFI/restricted-task blocks
dtrace -p PIDeven with SIP disabled, on Apple-signed binaries. Only-c CMDworks. - dtrace introduces Heisenbug similar to lldb's;
ustack(15-20)overhead measurable. - Workaround: probe libxpc C symbols, capture ustack, back-resolve Mercury offsets via per-run slide arithmetic.
Alternative trigger candidates (to investigate):
- A: Listener.deinit fires only on full Path-A; dtrace suppresses Path-A. Hard to falsify without non-perturbing instrumentation.
- B: HIT #18 trigger is UPSTREAM of Mercury Swift type destruction — possibly inside
_dispatch_mach_send_draintriggering kernel-side peer-cancel before any Swift destructor runs. Capturedreason=0x2MACH_EVENT enters libxpc with zero Swift frames in 100% of cases. - C: Peer cancel originates from CDS side first (D32 confirmed kernel-driven
MACH_NOTIFY_NO_SENDERS); devicectl-side listener release would be CONSEQUENCE not cause. This contradicts D32's "trigger in devicectl" inference (which was indirect).
Implication for D38 implementation: D37-D hook target should NOT be relied upon as primary without further empirical validation. Discriminator design [CD+0x30000, CD+0x32000) could not be empirically tested.
Successor research-probe options (all non-perturbing or static):
- CDS-side counter-instrumentation in
iosmux_inject.dylib— augment with mach-port-name tracking; on every accepted peer in CDS, log birth + cancel timestamps. Heisenbug-immune (in-process logging, no debugger). Reuses existing GAMBIT logging infrastructure. - Path-A reproduction without instrumentation — pure observation via CDS-side inject; observe whether listener-deinit fires by side-effect (peer-table walk in CDS-side instrument capturing whether NO_SENDERS arrives correlated with Path-A success log).
- Ghidra cross-binary rerun — load Mercury + CoreDevice + CDU + libxpc into one Ghidra project, full xref graph from libxpc's
_xpc_connection_remove_peer_implupward. Pure static. - dtrace USDT-only /
os_signpost— Apple's signpost surface available without per-instruction overhead; might catch listener-create / listener-destroy signposts.
Apparatus integrity: all four havoc binaries unchanged; iosmux_inject sha unchanged (52df2cc6...4803); iPhone (iosmux) connected (no DDI); CDS PID 1013 stable. MINOR non-destructive drift: a SECOND CDS instance (PID 10797) was launchd-spawned during probe window — both run concurrently; primary path unchanged. Zero new DiagnosticReports.
Distillation status: D37-E raw notes (notes/d37e-dtrace-listener-trigger.md, ~407 lines) deleted at distillation time. Per-run dtrace dumps preserved at /home/op/dev/iosmux/symbols/d37e-stack*.txt (host-side, not in repo). Verdict + symbol corrections + methodological findings + successor options preserved in this entry + aua-side-channel-mechanism.md §"D37-E update".
Q-D66-15 — D37-F (2026-05-02, cross-binary trigger search): Hypothesis B EMPIRICALLY FALSIFIED; D37-D survives; new G-4 alternative identified¶
D37-F extended D36's Ghidra project with libdispatch from havoc's DSC (libxpc extraction failed — ipsw/Sequoia 26.x format incompatibility). Searched all 4 client binaries + libdispatch for explicit cancel/release callers reachable from AUA flow. Apparatus integrity verified non-destructive.
Hypothesis B FALSIFIED: ZERO direct callers of xpc_connection_cancel, dispatch_mach_cancel, _xpc_connection_remove_peer*, or any other cancel/teardown-family API exist in CD/CDU/Mercury/devicectl reachable from [CD+0x30000, CD+0x32000). Only 3 cancel-method-impl trampolines exist in the entire process image (Mercury+0x4e589, Mercury+0x3d239, CDU+0x1509b9), all with ZERO callers in AUA flow. All 20 Mercury class deinit bodies enumerated; NONE contains explicit XPC cancel call. All XPC connection teardown is purely ARC-driven; libxpc's own dispose chain handles peer-table walk and NO_SENDERS propagation kernel-side without app-level help.
This collapses Hypothesis B into Hypothesis A (Heisenbug suppresses full Path-A under instrumentation) — no third path remains.
Hypothesis C INDETERMINATE: no CDS-targeting cancel-message in the 4 binaries identified, but cannot be ruled out without separate CDS-side Ghidra analysis. Mercury.XPCError.terminationImminent exists at Mercury+0x57dc0 but Mercury only RECEIVES it; never sends.
D37-D's hook target G-1 (Mercury+0x4e930) SURVIVES — D37-F's failure to find any other candidate strengthens its standing. It remains the ONLY Swift-level code site that performs _swift_unknownObjectRelease(listener_xpc) in the AUA flow.
NEW alternative G-4 — extra retain on AUA-completion context:
Hook the allocation site at CD+0x304a0+0x186 (the _swift_allocObject(...lVar17) call), insert an extra _swift_retain(lVar17) after construction. Keeps the context alive forever; Path-A's natural ARC release fails to drop refcount to 0; listener wrapper never deinits; no _swift_unknownObjectRelease(listener_xpc); no NO_SENDERS; no HIT #18; Path-B never fires.
Mechanism: prologue-patch CD+0x304a0 around offset +0x18a (just after alloc call), insert ~10 bytes that load alloc result and call _swift_retain before continuing. NO stack-walking discriminator needed.
Side effect: leaks one ~0x40-byte AUA-completion-context heap object + 1 anonymous listener OS_xpc_connection + associated mach ports per AUA invocation. devicectl is short-lived — bounded leak.
Tradeoff vs G-1: G-4 structurally simpler (no stack walk, no per-deinit filter), more deterministic, AUA-only by construction. G-1 more general but requires runtime stack discrimination D37-E couldn't empirically test.
Anti-recommendations:
- G-2 libxpc-internal
_xpc_connection_remove_peer_impl— UNVIABLE (private symbol, libxpc loads before injection, system-shared) - G-3
FUN_3b090helper called from listener.deinit — body not yet disassembled; potential follow-up if G-1/G-4 fail - DO NOT HOOK libxpc public stubs (
_xpc_connection_canceletc) — empirical absence of callers means hooks would not fire
Toolchain limit documented: ipsw cannot extract libxpc.dylib from macOS Sequoia 26.x DSCs (failed to rebase dylib via cache slide info: invalid command block size in record at byte 0x1208). libdispatch + most others extract cleanly. Future probes need Apple's full-Xcode dsc_extractor, arm64 DSC, or Apple's open-source libxpc source.
Apparatus integrity: all four havoc binaries unchanged; libdispatch successfully pulled from DSC; iosmux_inject sha unchanged (52df2cc6...4803); iPhone (iosmux) connected (no DDI); CDS PIDs 1013+10797 stable; zero new DiagnosticReports.
Distillation status: D37-F raw notes (notes/d37f-cross-binary-trigger.md, ~480 lines) deleted at distillation time. Cancel-callsite enumeration + Mercury all-deinits + libxpc internal symbols + AUA wrapper trace preserved at /home/op/dev/iosmux/symbols/d37f-*.txt (host-side, not in repo). DSC files at /home/op/dev/iosmux/dsc/ (~5.6 GB, host-side, not in repo). Verdict + G-4 + anti-recommendations preserved in this entry + aua-side-channel-mechanism.md §"D37-F update".
Q-D66-15 — D37-G (2026-05-02, CDS-side counter-instrumentation): Path-A NOT REPRODUCED; sub-ms peer teardown captured; D24-era 1001 sentinel GONE in current apparatus; §D-1 indeterminate (effect captured, cause requires devicectl-side probe)¶
D37-G extended iosmux_inject.dylib with ADDITIVE-only AUA peer lifecycle logging via new file iosmux_aua_peer_logger.m (~480 lines, 2 new DYLD_INTERPOSE entries on xpc_connection_cancel / xpc_release + gambit hook points for create/set_handler/activate/send/event). Built cleanly on havoc, deployed, captured 5 AUA invocations, restored D23 v2 baseline. Zero crashes, zero behavior change verified.
Sub-ms peer teardown timeline (5 runs avg, all Path-B):
t=0 gambit synthesizes success() reply on action XPC
t+~10ms gambit dials devicectl listener (peer create CDS-side)
t+~11ms gambit sends AFM
t+~12ms libxpc delivers "Connection invalid" event (171-872 µs after send)
t+~13-17ms devicectl "Failed to acquire usage assertion" (0.5-5 ms after libxpc event)
⅕ runs received Connection INTERRUPTED first then Connection INVALID (172 µs apart) — listener-side graceful tear-down. Other ⅘ went straight to INVALID — likely NO_SENDERS arriving as single mach event.
Path-A NOT REPRODUCED in 5 runs nor 24h history. Matches D29's 0/30 baseline. Brief's primary Q-G-1 (cancel BEFORE/AFTER "Successfully acquired") cannot be answered — no Path-A signal.
Surprising state finding: D24-era log line "Recieved error from side channel peer: XPCError(errorCode: 1001 ... peer[2019])" does NOT fire in current apparatus. The current Path-B failure surface is just useassertion: Failed to acquire usage assertion. The numeric 1001/3001 codes are constructed at higher Mercury/Swift error layers in devicectl, not at libxpc surface.
Same-image DYLD_INTERPOSE limitation: gambit's own xpc_connection_cancel + xpc_release calls bypass our interpose (calls within same image go direct to libxpc). Zero gambit-side cancel/release events captured — only Apple-framework cancel/release on the tracked AUA peer would be visible (none captured = consistent with D37-F finding that no Apple framework code explicitly cancels).
§D-1 status: INDETERMINATE. CDS-side observes EFFECT (peer's "Connection invalid" via libxpc) not CAUSE (devicectl-side ARC release of listener). The libxpc sentinel arrives in BOTH Path-A and Path-B cases — distinguishing requires Path-A run for direct comparison.
Two paths remain for definitive §D-1 answer:
- Path-A reproduction in current D23 v2 apparatus — rare (0/30 historical), unlikely without apparatus changes
- devicectl-side counter-instrumentation (D37-H?) — separate
iosmux_devicectl_inject.dylibper D33 P3 feasibility, hooksMercury.SystemXPCListenerConnection.deinitat Mercury+0x4e930 directly with in-process logging. Heisenbug-immune. Empirically validates §D-1 by observing deinit firing AND peer cancel within same process timeline.
Working-tree state: D37-G left additive source-tree changes uncommitted: inject/iosmux_aua_peer_logger.m (NEW), inject/{iosmux_gambit,iosmux_inject}.m + Makefile (additive edits). Deployed dylib on havoc is BASELINE (D23 v2). User decides commit/revert.
Apparatus integrity: iosmux_inject sha 52df2cc6...4803 (D23 v2) restored; iPhone connected (no DDI); CDS PID 13012 stable (was 1013, restarted during deploy); secondary CDS 10797 still running; zero new DiagnosticReports; pre-deploy snapshot at /home/op/backups/iosmux/d37g-pre-deploy/iosmux_inject.dylib.d23v2-presnapshot.
Distillation status: D37-G raw notes (notes/d37g-cds-peer-lifecycle.md, ~530 lines) deleted at distillation time. Captured JSONL (26 events × 5 runs) + unified log preserved at /home/op/dev/iosmux/symbols/d37g-*.{jsonl,txt} (host-side, not in repo). Verdict + timing + same-image limitation + next-step options preserved in this entry + aua-side-channel-mechanism.md §"D37-G update".
Q-D66-15 — D37-H baseline retest, post-VM-reboot (2026-05-02)¶
User rebooted havoc (took 2 attempts, minor hypervisor flake noted), re-armed apparatus via iosmux-restore.sh, ran AUA trigger on cold-boot apparatus to test whether D37-G's "Path-A absent in 24h+ continuous-arm baseline" was apparatus-state-dependent.
Result: PATH_B_ONLY. Cold-boot fresh apparatus produces byte-identical Path-B surface (Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3) ~14.7 ms after success() reply). Zero XPCError 1001 peer[2019] sentinel (matches D37-G observation that D24-era surface is gone).
Apparatus health verified: inject sha 52df2cc6...4803 (D23 v2 preserved), CDS PID rotated 1013 → 779 (clean reboot), iPhone (iosmux) connected (no DDI), tunneld healthy with new tunnel-port 52992 (per-session rotation per Q-D66-13).
Conclusion: D37-G's baseline IS apparatus-state-INVARIANT. Hypothesis "stale apparatus suppresses Path-A" is FALSIFIED. Path-B is deterministic in current D23 v2 + GAMBIT configuration regardless of how recently CDS / tunneld have been started.
Implication: D37-H or D38 can proceed with confidence that experiments will not be confounded by apparatus state drift. User chose D38 (implementation) over D37-H (more research).
Q-D66-15 — D38 G-4 (devicectl-side AUA-completion-context retain): FALSIFIED (2026-05-02)¶
Implementation of D37-F's G-4 hypothesis: insert extra _swift_retain(lVar17) after the _swift_allocObject that creates the AUA-completion context, hook target verified at CD+0x30791 (D37-F's "+0x186" was wrong — actual offset is +0x2f1, identified via D37-D Ghidra ownership trace lines 184-217).
Implementation: new iosmux_devicectl_inject.dylib injected via DYLD_INSERT_LIBRARIES wrapper at ~/iosmux/devicectl-wrapper.sh (D33 P3 re-confirmed feasible). 30-byte trampoline page (mmap RWX) calls original _swift_allocObject then _swift_retain on result, returns. Build sha 771a9471...673154, ad-hoc-signed.
Sanity check passed: 5 install banners fired correctly, patch-site bytes verified (e8 36 7d 31 00), rel32 reach 0xaf986a ≈ 11 MiB.
Empirical result: Path-B fired 9.9 ms after success() reply (unified.log 13:26:25.756664 → 13:26:25.766613). pathA_success=0, pathB_failure=1, XPCError1001=0. Identical surface to D37-H baseline.
FALSIFIED: D37-F's theory that lVar17 retention keeps the listener alive via lVar17+0x18 strong reference. Either lVar17+0x18 is NOT a strong reference, or another holder releases the listener regardless.
Q-D66-15 — D38 G-1 (devicectl-side listener.deinit body NOP): FALSIFIED (2026-05-02)¶
Pivot from G-4 to D37-D's primary cause-side recommendation: NOP the Mercury.SystemXPCListenerConnection.Cfd (deinit body) at Mercury+0x4e930 to prevent _swift_unknownObjectRelease(listener_xpc) from executing.
Implementation: same dylib pivoted. Mercury found at Tahoe cryptex path /Library/Apple/System/Library/PrivateFrameworks/Mercury.framework/... (suffix-match). Original 5 bytes at 0x4e930: 55 48 89 e5 49. Replacement: c3 90 90 90 90 (ret + nops). Function 40-byte body ending c3 at 0x4e957, padding to 0x4e960. Build sha 501a6f7b...b5108.
Sanity check passed: G-1 install banners present, patch-site verified at runtime VMA 0x10aab9000+0x4e930, write succeeded.
Empirical result: Path-B fired ~10 ms after success() reply, byte-identical surface to G-4. pathA_success=0, pathB_failure=1, XPCError1001=0.
FALSIFIED: D37-D's claim that 0x4e930 deinit body does the _swift_unknownObjectRelease(listener_xpc). Hypothesis: the release is in auto-generated __deallocating_deinit (0x4e960) not in the user body — pivoted to G-1'.
Q-D66-15 — D38 G-1' (devicectl-side __deallocating_deinit NOP): FALSIFIED (2026-05-02)¶
Surgical pivot from G-1: change patch site from Mercury+0x4e930 (body) to Mercury+0x4e960 (__deallocating_deinit Swift dealloc trampoline). Same 5-byte c3 90 90 90 90 patch.
Disasm of 0x4e960: byte-identical prologue to body (55 48 89 e5 49). Function does 3 indirect helper-release calls (auto-generated field releases) + tail-call to swift_deallocClassInstance(size=0x30, alignmask=0x7). Patching head with ret skips ALL helper releases AND the dealloc itself — Swift instance leaks forever, no field-walker fires.
Implementation: same dylib pivoted via single-offset change. Build sha 17b2950a...4b3e2.
Sanity check passed: G-1' install banners ([iosmux_d38_g1p]), patch-site verified at runtime 0x102a53000+0x4e960, write succeeded.
Empirical result: Path-B fired 11.0 ms after success() reply (unified.log 13:58:46.278517 → 13:58:46.289534). Same surface.
FALSIFIED: skipping the entire listener Swift class destruction does NOT prevent the kernel MACH_NOTIFY_NO_SENDERS cascade. Listener Swift wrappers at Mercury+0x4e930-0x4ea00 do not hold the mach send right that, when dropped, fires the kernel notification.
Q-D66-15 — empirical conclusion across D38 G-4 / G-1 / G-1'¶
Three structurally independent devicectl-side cause-side hooks on the listener Swift wrapper lifecycle (retain context referencing listener / NOP listener body / NOP listener dealloc trampoline) ALL fail with byte-identical Path-B surface (~10 ms latency, no D24-era 1001 sentinel). This is significant empirical evidence:
- Listener Swift wrappers in Mercury are NOT the holders of the send right that triggers kernel
MACH_NOTIFY_NO_SENDERS. - The actual holder is elsewhere — peer Swift wrapper, libxpc-internal C-level storage, or Swift continuation-thread cleanup are open candidates.
- D37-D's static map is INCOMPLETE on the critical path of the send-right release.
- D38 cause-side via Swift class lifecycle hooks is empirically dead end for listener wrappers.
User has explicitly directed against another iteration of forcing the Swift-wrapper-class lifecycle approach. After a week of cause-side iterations confirmed the wrapper-lifecycle wall is unbreakable, this is an undesirable path. Look for the gate, not break through the wall.
Status: Q-D66-15 cosmetic-accept REVERSED still stands per user directive. Direction shift to D39-research: fundamentally new angle, NOT another Swift-class-lifecycle iteration. Working hypotheses:
- Hook AUA wrapper FUNCTION ENTRY at CD+0x31750 (
acquireDeviceUsageAssertion(withReason:options:) async throws -> CoreDevice.DeviceUsageAssertion). Synthesise aDeviceUsageAssertionsuccess result directly. Skip the entire dispatch chain — bypass the race, not fight it. - Xcode-side direct intervention: hook DVTCoreDeviceCore's
_shadowUseAssertion.fulfilledsetter directly. Set true without ever invokingacquireDeviceUsageAssertion. Pure UI-gate flip. Requires DYLD_INSERT into Xcode itself. - Real Apple flow capture: D31 captured ONE success run on this apparatus — closer analysis of side-channel peer transcript may reveal what messages we're failing to send/respond-to.
- Symptom-side suppression (D33 Option 2 — DEPRIORITISED per user feedback rejecting filter-the-error approaches; the fix must address root cause, not consequences).
Apparatus integrity (D38 non-destructive): all Apple binaries unchanged on havoc; iosmux_inject.dylib still D23 v2 sha 52df2cc6...4803; new iosmux_devicectl_inject.dylib is opt-in via wrapper, direct xcrun devicectl invocations unaffected; zero CDS / devicectl crashes; zero new DiagnosticReports.
Source files committed at inject/iosmux_devicectl_inject.m (G-1' final state, sha 03dd3275...0c2e), scripts/iosmux-devicectl-wrapper.sh, and Makefile targets. Apparatus foundation for future devicectl-side experiments.
Full chain in aua-side-channel-mechanism.md §"D38 update".
Q-D66-15 — D39 H#2 deploy attempt + Xcode integrity incident + recovery (2026-05-02)¶
D39-research selected Xcode-side hasConnection.getter direct override at DVT+0x19e10 as the gate-not-wall pivot. Implementation produced iosmux_xcode_inject.dylib (initial sha 7f81c6c9...ddb02, lazy-load fix 9c76c383...) with 6-byte patch b8 01 00 00 00 c3 via in-memory mach_vm_protect VM_PROT_COPY.
Attempt 1 deploy bailed: [iosmux_d39] DVTCoreDeviceCore not in dyld image list — refusing to install. Cause: Xcode lazy-loads the framework only when the IDE first needs it; constructor's one-time scan ran before lazy-load. Surgical fix: replaced the scan with _dyld_register_func_for_add_image callback that fires for every image (already-loaded + future).
After fix deploy and quit-via-osascript, the next Xcode launch produced macOS Gatekeeper alert Xcode is damaged. Delete Xcode and download it again from the App Store. Most likely cause: macOS LaunchServices / taskgated / lsd / syspolicyd cache poisoned by DYLD_INSERT_LIBRARIES launch against the library-validated Xcode IDE binary even though amfi_get_out_of_my_way=1 boot-arg was active. D33 P3 had verified bypass for devicectl-class targets but Xcode-class has additional integrity layers.
Recovery confirmed 2026-05-02 ~16:25: user rebooted havoc, ran iosmux-restore.sh, Xcode launches normally with synthetic iPhone (iosmux) visible in Xcode UI. Sha audit 16:27 confirmed every Apple binary on havoc matches the host research copy at /home/op/dev/iosmux/binaries/ byte-for-byte — bundle integrity intact, alert was transient LaunchServices state poisoning cleared by reboot. Comprehensive bundle backup created at /home/op/backups/iosmux/recovery-2026-05-02/ covering all Apple bundles (binaries + _CodeSignature/CodeResources, including Xcode's 38 MB master Gatekeeper file) plus mirrors of all five iosmux-side dylibs.
Architectural lesson: amfi_get_out_of_my_way=1 is not a complete bypass for Xcode-class targets. Pre-launch sha baseline of every Apple bundle is mandatory before any future Xcode-side inject deploy. In-memory VM_PROT_COPY patching IS confirmed not to modify disk bytes — empirically verified by the post-recovery sha audit.
D39 H#2 source files (inject/iosmux_xcode_inject.m, scripts/iosmux-xcode-wrapper.sh, Makefile target, lazy-load fix) are committed. Future H#2 retest is gated on user-driven state-tagged protocol (D40 §E P2).
Q-D66-15 — D40 state-aware retrospective adopted (2026-05-02)¶
After D38's three-falsification cluster, user identified that probes across D20..D39 ran in DIFFERENT apparatus states without recording it. D31 Run #2 SUCCESS vs D29/D37-G/H 0/N likely reflects state-difference, not race jitter. D40 retrospective at notes/d40-state-retrospective.md (gitignored) maps every probe to its state-tuple where determinable, classifies findings as state-invariant (~14, durable binary facts) vs state-dependent (~8) vs unrecoverable (~4).
Memory rule feedback_state_tagged_testing.md adopted as project-wide policy: every AUA-related probe MUST tag its apparatus state at trigger. Cross-state generalisation of findings is empirically unjustified.
Top retest priorities (D40 §E):
- P1: D31 Run #2 reproduction across 6-cell Xcode-state matrix (S1-S6).
- P2: D39 H#2 validation across multiple states (NOT a single cell — declaring the gate works from one cell would replicate the D38 mistake). Currently blocked pending user-driven state-tagged retest.
- P3: D38 G-1' retest in Xcode-engaged cell. D38's "listener wrappers don't hold send right" verdict was Xcode=A only; may flip in hot state.
User explicitly took control of all GUI state transitions for forward retests. Dispatcher runs CLI batches only on user signal. State-batch script ready for state-tagged execution.
Full chain in aua-side-channel-mechanism.md §"D39 update" + §"D40 update".
Q-D66-15 — D40 P1 retest (state-tagged S1+S2+S3 baseline reproduction): all states deterministic Path-B, no SUCCESS reproduction (2026-05-02)¶
D40 P1 priority retest: reproduce D31 Run #2 SUCCESS path observation across the 3-state apparatus matrix. State labels:
- S1 (cold): post-VM-reboot + iosmux-restore.sh, Xcode NOT launched.
- S2 (warm Xcode): user launches Xcode from Dock, Devices and Simulators window open, iPhone (iosmux) row visible, Pair NOT clicked.
- S3 (post-Pair): same as S2 + user clicks iPhone row in Devices and Simulators, Xcode auto-fires AUA, "Failed to acquire assertion" appears in ERRORS AND WARNINGS UI section.
Dispatcher's original 6-cell taxonomy (S1-S6) was state-overengineering — empirical history split only on Pair-clicked-yes-no axis, not on Devices-window-open vs not. User corrected the taxonomy mid-test.
Per-state batch (/tmp/iosmux-d40-state-batch.sh <label>) ran C1 naked info details × 3 + C2 wrapped (G-1') info details × 3 + C3 naked list devices × 3 + C4 wrapped (G-1') list devices × 3:
| State | C1 naked info | C2 wrapped (G-1') info | C3 naked list | C4 wrapped (G-1') list |
|---|---|---|---|---|
| S1 (cold) | 0/3 + 3/3 | 0/3 + 3/3 | 0/3 listed 3/3 | 0/3 listed 3/3 |
| S2 (warm Xcode + Devices&Sims) | 0/3 + 3/3 | 0/3 + 3/3 | 0/3 listed 3/3 | 0/3 listed 3/3 |
| S3 (post-Pair) | 0/3 + 3/3 | 0/3 + 3/3 | 0/3 listed 3/3 | 0/3 listed 3/3 |
Cumulative: 36 (D29) + 6 S1 + 6 S2 + 6 S3 = 54/54 Path-B deterministic. No Path-A in any state. list devices confirmed non-AUA control: 18/18 deviceListed clean, no useassertion log events.
Verdict — D40 hypothesis FALSIFIED: apparatus state (cold / warm-Xcode / post-Pair-click) does NOT influence AUA outcome. D31 Run #2 SUCCESS = Heisenbug artifact (lldb perturbation timing altered ARC release sequence to give success-resume thread the win), NOT state-conditional reproduction. Production conditions without debugger hold Path-B 100% deterministically.
Implication: race-condition cannot be won via "catch the right state" — it's not state-dependent. Race outcome determined by libxpc/kernel timing, not GUI engagement. The Heisenbug verdict closes this branch of cause-side hypothesis space.
Q-D66-15 — D41 G-2 (devicectl-side peer __deallocating_deinit NOP at Mercury+0x4ea10): FALSIFIED across S1+S3 (2026-05-02)¶
D41 closed out D37-F's "send right holder is elsewhere" claim by exhausting the peer Swift class wrapper after D38 had falsified the listener wrapper triple. State-tagged across S1 (cold, Xcode not running) AND S3 (Xcode running + Devices and Simulators open + Pair clicked) per D40 protocol.
Hook target: Mercury.SystemXPCPeerConnection.__deallocating_deinit at Mercury+0x4ea10 (mangled _$s7Mercury23SystemXPCPeerConnectionCfD). Same 5-byte c3 90 90 90 90 patch as G-1' (just different offset). Verify-prologue passed (55 48 89 e5 49); patch landing confirmed via 6/6 install banners in S1-G2 and S3-G2.
Build sha: ab178ee577a1d2805c382fba9e4b39234e9aa081da5fec53aaa925a540d172a4. G-1' backup preserved at ~/backups/iosmux/d41-pre-deploy/iosmux_devicectl_inject.dylib.g1prime.
FAT-binary discovery: Mercury cryptex is FAT universal (x86_64 slice base at file offset 0x4000). Required 0x4ea10 vmaddr → 0x52a10 file offset for static dd-style verification. First D41 agent halted on SHA-1/SHA-256 false alarm before reaching this; re-dispatch with shasum -a 256 directive proceeded cleanly.
Empirical result:
| State | C2 wrapped Path-A | C2 wrapped Path-B |
|---|---|---|
| S1-G2 (cold) | 0/3 | 3/3 |
| S3-G2 (warm Xcode + Pair clicked) | 0/3 | 3/3 |
C1 naked baseline matches S1/S2/S3 (0/3 Path-A, 3/3 Path-B) — no apparatus drift. C3+C4 list devices show 3/3 deviceListed in both states — non-AUA path unaffected.
Verdict — G-2 FALSIFIED: peer Swift wrapper does NOT hold the mach send right that triggers MACH_NOTIFY_NO_SENDERS. Combined with D38's listener-wrapper triple falsification, all Mercury Swift wrappers reachable via DYLD_INSERT into devicectl are now exhausted. Send right held in libxpc-internal C state, third Mercury class, CoreDevice-side wrapper, or on the CDS half of the peer mach port pair.
Cumulative empirical baseline: 36 (D29) + 6 S1 + 6 S2 + 6 S3 + 6 S1-G2 + 6 S3-G2 = 66/66 Path-B deterministic across all states × all wrapper hook variants. Zero Path-A in any cell.
Apparatus integrity (D41 non-destructive): iosmux_inject sha unchanged (52df2cc6...4803), CoreDevice / Mercury / CoreDeviceUtilities unchanged, iPhone (iosmux) connected (no DDI) throughout, CDS PID 1535 stable across both batches, zero new DiagnosticReports.
Implication: cause-side via Swift wrappers in devicectl exhausted. Forward direction selected: Direction C-via-Ghidra (focused static disasm probe targeting DUA construction site + side-channel peer protocol + CDS-side cancel triggers) as research, then Direction A (daemon-side AUA forge in CDS) as implementation step depending on Ghidra findings. Direction B (H#2 persistent UI gate) deferred per D39 incident risk.
Distillation status: D41 raw notes (notes/d41-g2-peer-wrapper.md, ~237 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Empirical findings + state-tagged batch summaries preserved in this entry + aua-history-d38-d40.md §"D41 G-2 update".
Q-D66-15 — D42 (2026-05-02 evening, Ghidra static C-probe in S3 state): architectural inversion discovered + Q3 zero CDS-side cancel callers + DUA is NOT wire type — Direction A reframed, Direction D emerges¶
D42 dispatched as static-only Ghidra probe extending D36 project. First dispatch hit Anthropic usage-policy guardrails (initial brief framing read as offensive-tooling enumeration); reframed brief emphasising protocol-research-for-understanding completed cleanly under Opus model. Findings empirically reframe the forward roadmap.
Probe state: S3 (Xcode running, Devices and Simulators open, Pair clicked, "Failed to acquire assertion" error visible) — recorded per feedback_state_tagged_testing.md even though static disasm doesn't depend on apparatus state.
Q1 verdict — DUA is a CLIENT-SIDE wrapper class, NOT the wire Codable:
CoreDevice.DeviceUsageAssertionis a Swift CLASS (Hashable + Equatable, NO Codable conformance). 4 stored properties + computedstate: Stateenum.- Wire-format Codable is
CoreDevice.AssertionFulfilledMessageSTRUCT —(identifier: UUID, updatedSnapshot: <SomeStateSnapshot>). AFM is what flows on the side-channel peer; DUA is built CLIENT-SIDE by CD's listener handler at CD+0x2fb30 from a decoded AFM. - DUA construction site at CD+0x2ff02 (inside listener handler block); type-metadata accessor at CD+0xd3570; class layout uses runtime-patched field-offset globals at
0x41e8b0..0x41e8c8+0x3e7d80.
Implication: Direction A in original framing ("forge synthetic DUA in CDS") is empirically DEAD. DUA is not on the wire.
Q2 verdict — architectural inversion (CDS-as-client):
Side-channel peer architecture is INVERTED from initial assumption:
[devicectl/CD process] [CDS daemon process]
anonymous listener half ◄──── side-channel peer connection
(xpc_endpoint_create at CD+0x30afd) (xpc_connection_create_from_endpoint)
handler block: CD+0x2fb30 wraps via Mercury.{System|Remote}XPCPeerConnection
CDS receives the listener endpoint INBOUND via Input.endpoint field of action XPC, constructs peer half via xpc_connection_create_from_endpoint (3 call sites: 0x100020481, 0x10002120d, 0x10005e461). Mercury wraps via swift_dynamicCastClassUnconditional into SystemXPCPeerConnection or RemoteXPCPeerConnection. CDS never reaches Mercury.XPCSideChannel.anonymousListener() — that's CD/devicectl-side.
MACH_NOTIFY_NO_SENDERS fires on the listener (in devicectl) when the last peer-side send-right (in CDS) is dropped — empirically confirms the D29-D40 mechanism model. Send-right holder is unambiguously inside CDS's Mercury wrapper instance for the constructed peer.
Q2 verdict — protocol map:
| Direction | Message | Frequency |
|---|---|---|
| CDS → CD | AssertionFulfilledMessage(.success) Codable |
exactly once |
| CDS → CD | AssertionFulfilledMessage(.error) Codable |
once on failure |
| CD → CDS | (none — listener is receive-only from CD's POV) | — |
| Either side | (no termination msg) | lifecycle ends via send-right release |
Single-shot protocol per listener. No graceful shutdown handshake. CD listener block decodes wire enum: payload 1 → success path (DUA materialization), payload 2 → error path, > 1 → warning "Received > 1 assertion fulfillment", "Peer is unexpectedly nil" → if peer drops before AFM arrives.
Q3 verdict — ZERO CDS-side cancel callers:
Full enumeration of CDS binary found 0 direct call sites to any cancel/release/dispose API. CDS XPC API surface is 11 stubs total — none are cancel-family, none are send-message, none are listener-creation. CDS uses pure Swift-ARC for lifecycle.
Updated cancel-callers matrix across the bundle:
| Binary | xpc_connection_cancel | xpc_remote_connection_cancel | dispatch_mach_cancel |
|---|---|---|---|
| CoreDevice | 0 | 0 | 0 |
| CoreDeviceUtilities | 0 | 1 (DTRS-only, off AUA path) | 0 |
| Mercury | 1 (Mercury+0x4e589, unreachable) | 1 (Mercury+0x3d239, unreachable) | 0 |
| devicectl | 0 | 0 | 0 |
| CoreDeviceService | 0 | 0 | 0 |
The AUA peer connection is NEVER explicitly cancelled in any reachable code path. Lifecycle ends via send-right release through Mercury wrapper Swift-ARC deinit chains. CDS-side cause-side hook is empirically DEAD — nothing to hook in application code.
Forward design pivot — Direction D:
Wrapper-class lifecycle approach exhaustively falsified (D38 G-4/G-1/G-1' + D41 G-2 + D42 Q3 = 5 empirical dead-ends). Direction A as originally framed is dead. The remaining hypothesis with lowest cost + most direct gate-finding character:
Direction D — document StateSnapshot field set + verify GAMBIT AFM structural fidelity.
GAMBIT's current gambit_emit_afm_via_endpoint puts a DeviceStateSnapshot (from PairAction schema) into AFM's updatedSnapshot field, but the AFM Codable type expects some <SomeStateSnapshot> whose actual identity is !!! gap (TMA at CD+0x29bb30). If the expected type is NOT DeviceStateSnapshot, Apple's Codable decoder in CD's listener block fails on type mismatch → awaiter resolves with .failure → "Failed to acquire assertion" UI label. This is consistent with the 66/66 Path-B determinism observed across all states × inject loadouts.
D43 next step: focused Ghidra probe documenting the updatedSnapshot field's actual type + complete field set. After that, regenerate GAMBIT's AFM emission with structurally-correct payload, run state-tagged batch. If AFM decode succeeds in CD listener → DUA materializes → continuation resumes with success → race becomes moot.
Apparatus integrity (D42 non-destructive, S3 state): all Apple binaries unchanged, Ghidra project preserved, libxpc.dylib x86_64 slice newly available at /home/op/dev/iosmux/dsc/extracted-v3/, CDS binary now in /home/op/dev/iosmux/binaries/ (sha-verified). iPhone (iosmux) connected (no DDI) throughout, CDS PID 1535 stable, zero new DiagnosticReports.
Distillation status: D42 raw notes (notes/d42-ghidra-c-probe.md, ~620 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Per-question text dumps preserved at /home/op/dev/iosmux/symbols/d42-{q1,q2,q3}-*.txt (host-side, not in repo) — 40+ files documenting full DUA class metadata, construction site, peer connection construction sites, CDS XPC stub inventory, full CDS disasm. Empirical findings + architectural inversion + Direction D pivot preserved in this entry + aua-history-d38-d40.md §"D42 update".
Q-D66-15 — D43 (2026-05-03, S3 state, Ghidra static): TMA at CD+0x29bb30 IS DeviceStateSnapshot — D42 §G working hypothesis FALSIFIED¶
D43 was the focused Ghidra static probe Direction D promised. Goal: resolve the TMA at CD+0x29bb30 to a concrete Swift type, document its complete field set, and compare against GAMBIT's gambit_emit_afm_via_endpoint payload. Verdict: type identity matches, working hypothesis collapsed.
Type identity (resolved entirely from existing D42 dumps, no new disasm needed for §B.1): /home/op/dev/iosmux/symbols/CoreDevice-x86_64-demangled.txt:1724: 0x000000000029bb30 T type metadata accessor for CoreDevice.DeviceStateSnapshot. Conformances: Encodable, Equatable, Decodable. Module CoreDevice. Same type GAMBIT puts in AFM updatedSnapshot.
Field set: 3 stored properties matching auto-synth CodingKeys exactly: deviceInfo: CoreDeviceProtocols.DeviceInfo, capabilityImplementations: [CoreDevice.Capability:[Swift.String]], monotonicIdentifier: Swift.Int64. Plus 1 computed capabilities: Set<Capability> (getter-only, NOT in CodingKeys). NO state field on DSS.
GAMBIT comparison: GAMBIT emits 4 keys (state, capabilityImplementations, deviceInfo, monotonicIdentifier) — 1 spurious (state) + 3 matching. Auto-synth KeyedDecodingContainer<CodingKeys> ignores keys not in the CodingKeys enum, so the spurious state is decoder-tolerated noise, NOT decoder-poison. Type mismatch (DSS slot itself): NONE. Missing fields: NONE. Field-shape concern remained for capabilityImplementations (empty xpc_array — ambiguous shape) → punted to D44.
Verdict: D42 §G working hypothesis ("type mismatch causes Codable decode failure") FALSIFIED. Direction D's outer claim (different type) dead. Forward narrowed to inner question: does the empty capabilityImplementations shape fail the decoder?
Apparatus integrity (D43 non-destructive): static disasm only, zero apparatus interaction. inject sha unchanged.
Distillation status: D43 raw notes (notes/d43-statesnapshot-probe.md, 235 lines) deleted at distillation time. Disasm artifacts preserved at /home/op/dev/iosmux/symbols/d43-dss-*.txt (host-side, not in repo). Empirical findings preserved in this entry + aua-history-d38-d40.md §"D43 update".
Q-D66-15 — D44a (2026-05-03, Ghidra static, research): capabilityImplementations target shape = xpc_array-of-pairs; Capability is auto-synth Codable struct in CDU with 2 String CodingKeys¶
D44a was the cheap follow-up research probe to D43's outstanding field-shape concern, producing a ready-to-paste C/xpc-API recipe for D44b.
Top-level shape: xpc_array-of-pairs. DSS encoder at 0x29d6b0 selects the conformance witness SDyxq_GSEsSERzSER_rlMc — Swift stdlib's UNCONSTRAINED-key Dictionary<x, q>: Encodable where x: Encodable, q: Encodable. Cross-check: Capability's witness table in CDU lists VSEAAMc, VSHAAMc, VSLAAMc, VSQAAMc, VSeAAMc, Vs12IdentifiableAAMc, Vs23CustomStringConvertibleAAMc — no StringProtocol, no CodingKeyRepresentable → runtime cannot promote Capability into the keyed-by-string path → must use UnkeyedContainer with alternating [K0, V0, K1, V1, ...].
Capability identity: defined in CoreDeviceUtilities (CDU); auto-synth Codable struct with 2 stored properties (per field-descriptor cstring run at CDU+0x0026df50): name: Swift.String (CodingKey tag 0), featureIdentifier: Swift.String (tag 1). Confirmed via direct disasm of Capability.encode(to:) at CDU+0x1654b0 and init(from:) at CDU+0x165650. Wire form: xpc_dictionary { "name": <xpc_string>, "featureIdentifier": <xpc_string> }.
Recipe: 5-line C block using only xpc-API helpers already in iosmux_gambit.m (XPC.dict_create, XPC.dict_set_string, xpc_array_append_value, xpc_array_set_string). Ready for D44b paste.
Apparatus integrity (D44a non-destructive): static disasm only.
Distillation status: D44a raw notes (notes/d44a-capabilityimpl-shape.md, 257 lines) deleted at distillation time. Disasm artifacts preserved at /home/op/dev/iosmux/symbols/d44a-cap-*.txt (host-side). Recipe + shape determination preserved in this entry + aua-history-d38-d40.md §"D44a update".
Q-D66-15 — D44b (2026-05-03, S1 state, c-developer impl): apparatus learning — env-gate via launchctl setenv does NOT propagate to CDS XPC service post-killall on Tahoe 26.x¶
D44b was the first attempt at the field-shape falsifier. Built D44a's recipe under env-gate IOSMUX_D44_CAPIMPL=1. Build / link / codesign clean.
Failure mode: agent set env via ssh havoc-root launchctl setenv IOSMUX_D44_CAPIMPL 1 BEFORE killall, triggered devicectl. Verified empirically via launchctl procinfo on the respawned CDS PID — IOSMUX_* was absent from the env vector. The env-gated branch never executed ((no D44b lines) in inject log). Unified-log result BY CONSTRUCTION identical to empty-array baseline — falsifier never ran.
Cause: NOT SIP (user clarified SIP is OFF on havoc; csrutil status: disabled). Earlier dispatcher speculation about "SIP-filtered env" was incorrect. Actual cause unknown — possibilities include launchd caching env at service registration (which killall does not re-trigger), user-domain vs system-domain bootstrap mismatch, or other Tahoe 26.x-specific launchd behavior. No further investigation; symptom-only is sufficient apparatus learning.
Apparatus integrity (D44b non-destructive): D23 v2 baseline restored cleanly via backup; post-restore sha 52df2cc6...4803. iPhone connected (no DDI). 0 new DiagnosticReports.
Distillation status: D44b raw notes (notes/d44b-fieldshape-falsifier.md) deleted at distillation time. Apparatus learning captured in memory rule feedback_no_env_gate_for_cds_xpc. Empirical findings preserved in this entry + aua-history-d38-d40.md §"D44b update".
Q-D66-15 — D44b' (2026-05-03, S1 state, c-developer impl, unconditional retest): Direction D FALSIFIED — capabilityImplementations field-shape NOT the gate; AUA Path-B determinism lives on a different surface¶
D44b' re-ran the falsifier with the env-gate removed — new non-empty capabilityImplementations path runs unconditionally on every gambit_build_dss_only call. Mandatory restore via backup at end of probe.
Inject log confirms new path executed: /home/op/backups/iosmux/d44b-pre-deploy/iosmux_inject.log.d44b-prime line 689 — [inject] GAMBIT: D44b unconditional capImpl entry emitted, sandwiched between AUA reply-synth (line 688) and AFM emit (line 690). The xpc_array sent on the wire contained one (Capability, [String]) pair per D44a recipe.
Unified log: ZERO Codable decoder errors. Verbatim grep of 60s window for Failed to acquire, typeMismatch, keyNotFound, dataCorrupted, Codable, Capability, capabilityImplementations, CoreDeviceError returned EXACTLY ONE matching line — the same CoreDeviceError(errorCode: 3, ... "Failed to acquire assertion") that fires for the empty-array baseline. No decoder errors anywhere.
Critical sequencing (lines 22-29 of /tmp/iosmux-d44b-prime-unified-log.txt): [action] Invoking AcquireDeviceUsageAssertion (t=11:32:06.598) → Forwarding to ExecutionLocation.coreDeviceService → Forwarding to ExecutionLocation.elsewhere(<SystemXPCPeerConnection ... pid = 2222 ...>) → Received reply from forwarded action: SUCCESS() (t=11:32:06.602) → Received reply from action: SUCCESS() → 2 analytics lines → [useassertion] Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3, ...) (t=11:32:06.615570). The xpc-Codable decoder accepted GAMBIT's reply (success()); the error fires ~13 ms later on the useassertion subsystem — NOT the action / decoder subsystem. This is post-decode logic that consumes the DSS payload and decides "no assertion was acquired."
Verdict — Direction D dead. AUA Path-B determinism does NOT live on capabilityImplementations shape. D43 §Recommendations option (1) FALSIFIED. Two surviving candidate surfaces: (a) different DSS field consumed post-decode by useassertion subsystem (e.g. DeviceInfo sub-field invariant, monotonicIdentifier value-check); (b) non-Codable devicectl-side state-machine check ignoring DSS shape entirely (e.g. AFM identifier UUID compared against client-tracked assertion-instance UUID — D30 admonition hypothesis #1, never empirically tested).
Cumulative falsifications across both axes: 5 cause-side (D23 PARTIAL + D30 + D38 G-4/G-1/G-1' + D41 G-2) + 2 payload-fidelity (D43 type / D44b' shape) = 7 empirical falsifications. Q-D66-15 forward direction = REFRAME REQUIRED.
Apparatus integrity (D44b' non-destructive): D23 v2 baseline restored cleanly. Pre-sha = 52df2cc6...4803, during-active sha = a02fb802...2f03, post-restore sha = 52df2cc6...4803 (MATCH). 0 new DiagnosticReports. iPhone connected (no DDI) post-restore. Working tree clean.
Distillation status: D44b' raw notes (notes/d44b-prime-fieldshape-falsifier.md, 235 lines) deleted at distillation time. Empirical record + apparatus telemetry preserved at /tmp/iosmux-d44b-prime-*.{log,txt} host-side (transient). Inject log archived at /home/op/backups/iosmux/d44b-pre-deploy/iosmux_inject.log.d44b-prime. Verdict + critical sequencing + remaining candidates preserved in this entry + aua-history-d38-d40.md §"D44b' update".
Q-D66-15 — D45 (2026-05-03, Ghidra static, research-only): D30 hypothesis #1 FALSIFIED; PIVOT — useassertion subsystem string lives ONLY in CoreDeviceService, not CD/devicectl¶
D45 was the cheap occam's-razor probe closing the oldest standing AUA hypothesis (D30 admonition #1, untested through ~14 follow-up probes D31..D44b'). Goal: determine whether Apple's AUA wrapper generates and tracks a per-assertion-instance UUID separate from CoreDevice.deviceIdentifier that GAMBIT (which currently echoes deviceIdentifier into AFM identifier) cannot know about. Verdict: no such UUID exists on the AUA endpoint side-channel path, plus a Q4 architectural pivot that reshapes the D46 probe target.
Q1 — AUA wrapper UUID construction: confirmed Tahoe 26.x offsets — async body at CD+0x317a0 (entered via swift_task_switch from prologue at 0x31750), completion-handler-flavoured body at CD+0x304a0. Neither calls uuid_generate_random / CFUUIDCreate / Foundation.UUID.init(). Verified by full disasm of both functions (artifacts at /home/op/dev/iosmux/symbols/d45-aua-async-body-clean.txt + d42-q1b-cb-acquire.txt D42-era). No per-call UUID minted client-side before the action goes on the wire.
Q1 — DUA construction at AFM-decode site: D42's disasm dump of CD+0x2fb30..0x2ff3d shows the inbound AFM dispatch is a swift_getEnumCaseMultiPayload(decoded_AFM, type_metadata) switch (case 0 = .success(AFM), case 1 = .error). The success branch loads three quadwords from the AFM payload (UUID lo64 / hi64 / updatedSnapshot reference), allocates a fresh DeviceUsageAssertion instance via swift_allocObject, then copies the 16-byte UUID from the AFM identifier slot into DUA.identifier via UUID value-witness initializeWithCopy (vwt+0x10). NO swift_stdlib_memcmp / explicit cmp / equality branch exists between the success-case entry (0x2fde9 → 0x2febd) and the DUA construct (0x2ff02..0x2ff3d). The DUA's identifier is purely a mirror of whatever the AFM payload carried.
Q1 — IdentifiableAssertionDetails is parallel-surface, not on AUA path: CD does export CoreDevice.IdentifiableAssertionDetails.init(identifier:owningProcess:processName:creationReason:hostName:preparednessDescription:invalidationHandler:) at CD+0x290330, and CoreDevice.TunnelAssertionGroup.identifiableAssertions: [UUID: IdentifiableAssertionDetails] at CD+0x2904a0. But this is the tunnel-management API (TunnelAssertionRequest.identifiable(...)), not the Mercury action-invocation surface AUA uses. The AUA action declaration's Input has exactly 3 fields — reason: String, options: UsageAssertionOptions, endpoint: __C.OS_xpc_object (confirmed at /home/op/dev/iosmux/symbols/CoreDevice-x86_64-demangled.txt:1462-1471); no identifier, no IdentifiableAssertionDetails. Pattern scan for direct call rel32 to CD+0x290330 returned 0 hits in CD's text segment — IAD is reached only via Swift PWT dispatch on a code path parallel to AUA's endpoint side-channel.
Q3 — reachability from GAMBIT: AUA invocation envelope has no per-assertion UUID. Top-level keys are the standard 6 already documented (actionIdentifier, invocationIdentifier, deviceIdentifier, coreDeviceVersion, CoreDeviceDDIProtocolVersion, input). Even if the dispatcher chose to swap GAMBIT's AFM identifier from deviceIdentifier to invocationIdentifier, the post-decode failure would not change — nothing on the CD-side AFM-decode path compares this UUID to anything.
Q4 PIVOT — useassertion subsystem lives in CDS, not in CD/devicectl: critical architectural reframing. grep -ac useassertion enumeration across the bundle (/tmp/claude-1000/.../tasks/{bv92g4hhs,b117pzx95}.output):
| Binary | hits |
|---|---|
| CoreDevice | 0 |
| CoreDeviceUtilities | 0 |
| Mercury | 0 |
| devicectl | 0 |
| CoreDeviceService | >0 (subsystem string at slice file offset 0xb7d90, VA 0x1000b7d90) |
The [useassertion] Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3, ...) log line in D44b' unified log is therefore emitted server-side (CDS), not client-side (devicectl). This contradicts the D44b' anchor framing of "non-Codable devicectl-side state-machine check" — the surviving candidate (b) is CDS-side.
Strong CDS-side candidate strings on the AUA failure path (slice file offsets):
| CDS+offset | String |
|---|---|
0xbbd30 |
"Cannot acquire a usage assertion on a device without an effective device identifier." — strongest gate candidate |
0xbc030 |
"This device does not support acquiring a usage assertion." — capability gate |
0xbbef0 |
"Acquired usage assertion." — success log |
0xc2e40 |
format: "Received request to create usage assertion %{public}s for device %{public}s, (required preparedness=%ld, owning pid=%d, reason=%s)" — the assertion-id is generated server-side at request receipt; logged but never echoed to client |
0xc3160 |
format: "Notifying client of invalidation for usage assertion %{public}s, device %{public}s. Error: %s" |
CDS-side handler symbols (in /home/op/dev/iosmux/symbols/d45-cds-demangled-x86_64.txt):
CoreDeviceService.AcquireDeviceUsageAssertionActionImplementationnominal type descriptor at0x1000b6404, conformance to aCoreDeviceUtilities.ActionImplementation-style protocol at0x1000b641e.CoreDeviceService.ListUsageAssertionsActionImplementationcompanion at0x1000b640a.CoreDeviceService.InProgressServerAssertionObj-C class at0x1000b6386(__DATAat0x1000d2e68, IVARS at0x1000d2048) — the server-side per-assertion-bookkeeping object.CoreDeviceService.RemotePairingDeviceRepresentationconforms toCoreDevice.UseAssertionProvidingDeviceRepresentationat0x1000b7324.
Tooling gap surfaced: Tahoe 26.x CDS uses chained-fixup encoding for lea rip+rel32 references to __TEXT,__cstring and for cross-segment call rel32. Raw byte-pattern xref scans to 0x1000b7d90 (subsystem string) returned 0 hits. D46 needs Ghidra (D36.gpr already imports CDS) or llvm-objdump --chained-fixups for chained-fixup-aware xref analysis.
Verdict — D30 hypothesis #1 FALSIFIED: assertion UUID == AFM-carried UUID; CD-side decoder accepts unconditionally; GAMBIT current behaviour is correct. Direction E (assertion-instance UUID mismatch) is empirically dead as a separate axis.
Verdict — Q4 architectural pivot: errorCode 3 emission is CDS-side, not devicectl-side. Forward narrows to Direction F = CDS-side useassertion subsystem trace. Probable gate: post-success()-reply check on the AcquireDeviceUsageAssertionActionImplementation run/execute path that fires the 0xbbd30 "effective device identifier" log/error. D46 scope.
Cumulative falsifications: 5 cause-side (D23 PARTIAL + D30 + D38 G-4/G-1/G-1' + D41 G-2) + 2 payload-fidelity (D43 type / D44b' shape) + 1 identifier-mismatch (D45 D30 #1) = 8 empirical falsifications.
Apparatus integrity (D45 non-destructive): pure host-side static disasm; zero apparatus interaction. inject sha unchanged at 52df2cc6...4803. iPhone untouched. Working tree clean post-distill.
Distillation status: D45 raw notes (notes/d45-aua-identifier-mismatch.md, 367 lines) deleted at distillation time. Disasm artifacts preserved at /home/op/dev/iosmux/symbols/d45-{aua-async-body-clean,aua-input-encode-clean,aua-input-decode-clean,cds-nm-x86_64,cds-demangled-x86_64}.txt (host-side, not in repo). Strings dump at /tmp/claude-1000/.../tasks/b117pzx95.output (transient). Empirical findings + Q4 architectural pivot + D46 prep checklist preserved in this entry + aua-history-d38-d40.md §"D45 update".
Q-D66-15 — D46 (2026-05-03, Ghidra static probe, research-only): Direction F CONFIRMED + bridgeable; firing site = FUN_100020700; gate = _get_effectiveDeviceIdentifier == .none¶
D46 was the wide-variant Ghidra static probe Direction F promised post-D45 Q4. Goal: trace the post-success()-reply CDS-side code path that emits errorCode 3 ~13 ms after the Codable decoder accepts GAMBIT's reply, identify the failing predicate, characterise "effective device identifier" semantics, and produce a D46b implementation roadmap with concrete hook targets.
Verdict — Direction F CONFIRMED + bridgeable. The gate predicate is CoreDevice::ServiceDeviceRepresentation::_get_effectiveDeviceIdentifier(this, out) returning Optional<DeviceIdentifier>.none. On .none the AUA action body throws CoreDeviceError.deviceRepresentationMissingIdentity via _swift_willThrow / _Continuation._resume(error) — that is the errorCode 3 seen in D44b' line 29.
Firing function: FUN_100020700 at slice VA 0x100020700 in CDS (size 0x11fc), called from action dispatcher FUN_100018ea0 at 0x100018ea5 (UNCONDITIONAL_CALL). This is the AUA action run/execute body. Identified via combination of (a) IdentifiableAssertionDetails type-metadata accessor calls, (b) AcquireDeviceUsageAssertionActionDeclaration::Input field getter calls (get_options, get_reason, _get_endpoint), © exclusive caller from FUN_100018ea0.
Cited code-site addresses (slice VAs):
0x100020700—FUN_100020700AUA actionrunbody (firing function).0x100020cff— predicate call site_get_effectiveDeviceIdentifier(this, out).0x100020d5d—_get_deviceRepresentationMissingIdentityinvocation.0x100020df3—_Continuation::_resume(error)(throw point).0x100018ea0/100018ea5— action router → AUA-body dispatch.0x10001fa00—FUN_10001fa00AFM-emit happy path (post-gate-pass).
Calibration fix vs D45 anchor: the AUA-fail-EDI string lives at slice VA 0x1000b7d30 (slice file offset 0xb7d30). D45's CDS+0xbbd30 was the FAT-archive-relative offset (x86_64 slice starts at FAT offset 0x4000). Other D45 strings (0xb7d90 subsystem, etc.) were already slice-relative. D45 historical entry above is preserved as-is; forward-looking docs (anchor + Q-D66-15 row) use D46-corrected values.
Why direct-LEA xref scans returned 0 hits in D45: Swift compiler packs __cstring densely and constructs error messages at runtime via Swift String length-prefixed encoding from a SHARED SUFFIX 0x1000b89e0 ("an effective device identifier."). 8 LEAs into the suffix appear across 4 sibling gates (DDI-disable, DDI-enable, Pair, Tunnel-create) plus the AUA path itself — confirming the same _get_effectiveDeviceIdentifier gate fires uniformly across CDS for ALL action handlers that need an SDR.
"Effective device identifier" semantics: ServiceDeviceRepresentation._effectiveDeviceIdentifier: Optional<DeviceIdentifier> is a Swift property on the CDS-internal SDR populated post-RSD-pair. Distinct from inbound Context.deviceIdentifier UUID and from pre-pair RemoteDevice.deviceIdentifier. Returns .none when (a, most likely) the SDR for the requested UUID is registered but its post-pair RSD lookup hasn't populated the field yet, or (b, less likely) explicit invalidation marker is set. iosmux's existing inject chain does NOT touch this CDS-side field — explains why all 8 prior falsifications missed the real gate.
Architectural advantage — bridgeable from existing inject: _get_effectiveDeviceIdentifier is an EXTERNAL symbol in CoreDevice.framework. CDS imports CoreDevice via LC_LOAD_DYLIB, and so does our existing iosmux_inject.dylib. The getter is reachable via dlsym(RTLD_DEFAULT, "<mangled>") from inside the inject — same address space, same mechanism that's already proven for the S1.B trampoline pattern at inject/iosmux_inject.m:1463-1525. No new injection target needed.
Recommended D46b/c hook target: _get_effectiveDeviceIdentifier getter. Hook payload: call original; if Optional tag byte indicates .none, copy <context.deviceIdentifier> UUID into output buffer, set tag = .some. S1.B trampoline pattern. Major risk: hook fires for ALL 4 sibling gates (DDI-disable/enable, Pair, Tunnel-create) plus AUA — may need stack-frame discriminator on hook entry.
Two alternative hook targets evaluated and rejected as more brittle:
- Target #2 —
FUN_100020700whole-prologue replacement (4.6 KB body re-implementation; high Swift-runtime fragility). - Target #3 —
_get_deviceRepresentationMissingIdentityconstructor hook (wider blast radius; throw suppression at constructor time is harder than short-circuiting the predicate).
InProgressServerAssertion lifecycle: the Obj-C class at CDS+0x1000b6386 is allocated in FUN_10001fa00 AFTER the gate at Stage 4 passes — does NOT exist at gate-trip time. Hooking IPSA does NOT help; the gate is upstream. IVAR layout documented in aua-history-d38-d40.md §"D46 update" Q5 for D46b reference.
D46b prep gaps (research-only dispatch needed before D46c implementation): (1) exact mangled symbol via swift demangle over nm CoreDevice, (2) disassembly of getter prologue (need ≥13 bytes clean linear, no PC-rel, no PAC), (3) confirm getter is not fully inlined under -O3, (4) verify exact Optional<DeviceIdentifier> ABI shape (assumed 16+1 bytes for UUID+tag).
Tooling stack: 8 Java GhidraScripts authored (PyGhidra mode required Java port — Jython removed in Ghidra 12). Ghidra D36 project now contains imported CoreDeviceService (x86_64, all 2987 chained-fixup pointers resolved at import). 150 423 instructions in __text walked; 269 distinct cstring xref targets enumerated; 10 enclosing AUA-relevant functions decompiled (8344 lines of decompile output). llvm-objdump --chained-fixups not used — Ghidra resolved fixups at import.
Apparatus integrity (D46 non-destructive): pure host-side static disasm; zero apparatus interaction. inject sha unchanged at 52df2cc6...4803. iPhone untouched. Working tree clean post-distill.
Distillation status: D46 raw notes (notes/d46-cds-useassertion-trace.md, ~430 lines) deleted at distillation time. Ghidra-script artifacts at /tmp/iosmux-d46-*.{sh,java,py,txt} (transient). Full empirical decompile at /tmp/iosmux-d46-q2-funcsearch.txt (8344 lines, transient). Verdict + firing site + predicate + D46b prep checklist preserved in this entry + aua-history-d38-d40.md §"D46 update" + aua-side-channel-mechanism.md §"Forward direction — Direction F CONFIRMED + bridgeable post-D46".
Q-D66-15 — D46b (2026-05-03, Ghidra static probe, research-only): D46c READY-WITH-CAVEATS — ¾ prep gaps closed; D46 hook recipe overridden via capture-and-replay¶
D46b was the prep dispatch closing the 4 verification gaps D46 surfaced before D46c implementation. Goal: confirm exact mangled symbol of _get_effectiveDeviceIdentifier, byte-level prologue disassembly, inline status under -O3, and exact Optional<DeviceIdentifier> ABI shape — so D46c can be specified without remaining unknowns. Pure host-side static disasm via Ghidra D36 project (CoreDevice imported as Q1 first step) + nm/swift-demangle/llvm-objdump.
Verdict — D46c READY-WITH-CAVEATS. ¾ D46-Q6 prerequisite gaps RESOLVED + 1 PARTIAL (with empirical fallback). Several CRITICAL CORRECTIONS to D46's plan:
Q1 RESOLVED — exact mangled symbol = _$s10CoreDevice07ServiceB14RepresentationC09effectiveB10IdentifierAA0bF0OSgvgTj at slice VA 0x28db10 in CoreDevice. Demangled: dispatch thunk of CoreDevice.ServiceDeviceRepresentation.effectiveDeviceIdentifier.getter : CoreDevice.DeviceIdentifier?.
Critical correction vs D46 candidate symbol: D46 proposed _$s10CoreDevice32ServiceDeviceRepresentationP012_effectiveB10IdentifierAA0B10IdentifierVSgvg (with P for protocol and V for struct). This was WRONG in 3 places — ServiceDeviceRepresentation is class (C), DeviceIdentifier is enum (O, per D45 §Q1.3), and only the dispatch thunk (vgTj) is externally exported (bare getter vg is NOT in llvm-nm -a output). CDS imports the thunk by name via chained-fixup name_offset = 29559.
Q2 RESOLVED — prologue OK. Thunk has 15 contiguous clean linear bytes before JMP RCX terminator: PUSH RBP / MOV RBP,RSP / MOV RCX,[R13] / MOV RCX,[RCX+0xd8] / POP RBP. No PC-rel, no PAC. S1.B trampoline pattern at inject/iosmux_inject.m:1463-1525 viable. Sample bare getter at 0x28a440 (selfReportedDeviceIdentifier) shows PC-rel ADD at offset 7 + LEA at offset 14 — only ~6 clean bytes; bare getters NOT trampoline-safe; thunks ARE.
Q3 RESOLVED — getter callable via thunk. Bare getter not externally exported but thunk IS. CDS calls via CALL 0x1000b0a56 (CDS-internal stub → thunk in CoreDevice) at CDS:0x100020cfa. Inlining-of-bare-getter status irrelevant — thunk is canonical externally-callable dispatch entry. Witness-table alternative NOT NEEDED — ServiceDeviceRepresentation is class (vtable dispatch), not protocol (witness-table dispatch).
Q4 PARTIAL — Optional0x100020d20: CALL [R12 + 0x30] (VWT slot +0x30 = getEnumTagSinglePayload) followed by CMP EAX, 0x1; JNZ — Swift uses runtime VWT call for Optional tag inspection. No fixed byte-tag offset. The naive D46 hook recipe (if (out[16] == 1) { memcpy(out, uuid, 16); out[16] = 0; }) will NOT work. D46c MUST use either VWT machinery OR capture-and-replay strategy (preferred — layout-agnostic).
Calling convention correction (R6): Swift class-method getter on x86_64 is non-cdecl — R13 = self, RAX = indirect-result buffer. D46 had assumed rdi/rsi. D46c hook entry MUST use inline-asm preamble to capture R13 and RAX before any C code runs.
D46c hook recipe — capture-and-replay strategy (rewritten from D46's incorrect proposal): at inject load, dlsym thunk symbol + install S1.B trampoline (15 saved bytes). On every hook entry: capture R13/RAX via inline asm; call original via trampoline; inspect Optional tag via getEnumTagSinglePayload; if .some and not yet captured, snapshot the buffer to global static; if .none and a .some is captured, destroy current .none payload (VWT slot +0x08) + initializeWithCopy from captured buffer (VWT slot +0x10). This avoids constructing DeviceIdentifier.uuid(...) from scratch and is layout-agnostic.
Updated risk register (D46-R1 + D46b-R5..R11):
- R1 (carried): hook fires for ALL 4 sibling gates (DDI-disable/enable, Pair, Tunnel-create) plus AUA. Mitigation: stack-frame return-address discriminator (TODO-D), only override when caller is
FUN_100020700(CDS:0x100020700..0x1000218fc); ~10 lines C. - R5: Optional uses VWT runtime tag — capture-and-replay sidesteps.
- R6: non-cdecl ABI — inline-asm preamble at hook entry.
- R7: bootstrap
.somebuffer — capture from first natural.someon sibling gate before AUA's first attempt. - R8: hook recursion if buffer ops trigger another
effectiveDeviceIdentifiercall — per-threadin_hookflag. - R9:
dlsym(RTLD_DEFAULT, <Swift mangled>)may behave unexpectedly — fallback via_dyld_get_image_*exports trie walk. - R10: constructing
DeviceIdentifierfrom scratch needs case-constructor symbol — capture-and-replay obviates the need. - R11:
selfReportedDeviceIdentifiersibling at0x28a440/thunk0x183510could be backup target ifeffectiveproves unreplaceable.
D46c open TODOs: TODO-A (capture-and-replay primary), TODO-B (dlsym fallback), TODO-C (Optional<T> size via swift_getGenericMetadata), TODO-D (sibling-gate return-address discriminator), TODO-E (per-thread recursion guard).
Apparatus integrity (D46b non-destructive): pure host-side static disasm + Ghidra invocation. Zero apparatus interaction. Zero binaries modified. inject sha unchanged at 52df2cc6...4803. iPhone untouched. Working tree clean post-distill.
Distillation status: D46b raw notes (notes/d46b-prep-gaps.md, 915 lines) deleted at distillation time. Ghidra script artifacts at /tmp/iosmux-d46b-*.{sh,java,txt,log} (transient). Verdict + corrections to D46 + D46c TODOs preserved in this entry + aua-history-d38-d40.md §"D46b update" + aua-side-channel-mechanism.md §"D46b-resolved hook target".
Q-D66-15 — D46c + D46c-fix (2026-05-03, c-developer impl + delta dispatch): hook code authored + build/deploy clean; BLOCKED on Swift metadata-accessor resolution; Tahoe 26.x dyld shared cache empirical finding¶
D46c (c-developer, implementation dispatch) authored the full hook in inject/iosmux_aua_edi_hook.{h,m} per D46b spec. Build clean on havoc, codesign clean, deploy clean to /Library/Developer/CoreDevice/iosmux_inject.dylib, CDS-stable post-respawn (PIDs 8086, 8198 across two deploy attempts). Devicectl trigger ran end-to-end, exit 0. Hook never armed — install function bails when dlsym(RTLD_DEFAULT, "_$s10CoreDevice16DeviceIdentifierOMa") returns NULL. Scoped dlsym(dlopen("/Library/Developer/PrivateFrameworks/CoreDevice.framework/CoreDevice", RTLD_NOLOAD), ...) also returned NULL. The DeviceIdentifier type metadata accessor is not in CoreDevice's exports trie (D46b R9 empirically realized).
D46c-fix (c-developer, delta dispatch) added a Mach-O LC_SYMTAB walker as fallback: find_coredevice_image_index() (exact trailing-component match /CoreDevice to avoid CoreDeviceUtilities) + resolve_via_symtab(target) (locates LC_SYMTAB load command, maps __LINKEDIT segment, iterates nlist_64 entries skipping N_STAB). Build clean (sha 0f5482af...), deploy clean. Per-image LC_SYMTAB also blank — 5281 nlist entries iterated correctly; zero matches for the target.
Architectural finding — Tahoe 26.x dyld shared cache strips local symbols. CoreDevice on Tahoe 26.x lives in the dyld shared cache. The cache builder strips per-image LC_SYMTAB local-symbol entries and consolidates them into a separate cache-side symbol-info file. Both dlsym (exports trie) AND direct nlist_64 walk (per-image LC_SYMTAB) return blank for internal Swift symbols (e.g. type metadata accessors _$s..OMa for non-public types). This affects any future symbol resolution against framework internals; captured in memory rule feedback_dyld_shared_cache_strips_symbols.
Forward paths post-D46c-fix (existing hook code dormant + ready, activates with one-line install-function patch once either path lands):
- Path #2 — Hardcoded metadata-accessor offset (Ghidra-derived): mirrors existing project pattern at
inject/iosmux_aua_keepalive.m:55(IOSMUX_AUA_CD_REMOVECACHED_OFFSET). Two dispatches: D46d (research-only,general-purposeagent) — Ghidra/llvm-objdump on extracted-from-shared-cache CoreDevice binary (the on-disk artifact retains symbol info) → derive slice VA of_$s10CoreDevice16DeviceIdentifierOMa; D46e (impl,c-developeragent) — replace install resolution withg_did_meta = ((MaFn)((uint8_t *)cd_base + OFFSET))(). Cost: ~5 lines C diff + re-derivation per CoreDevice version bump. - Path #3 — Dynamic metadata recovery via Swift runtime: single c-developer dispatch — hook captures
R13(self) on first entry, walks*self → metadata pointer → vtable → ...to recoverOptional<DeviceIdentifier>metadata indirectly. Version-resilient; higher implementation risk (Swift runtime ABI undocumented for non-public types). 50-100 lines.
Path #2 recommended (project precedent, lower risk, faster).
Apparatus integrity: D23 v2 baseline 52df2cc6...4803 preserved through D46c + D46c-fix. Pre-deploy backup verified pre + post-restore. Zero CDS crashes. Zero new DiagnosticReports. iPhone connected (no DDI) throughout. Working tree clean post-distill (with hook code authored in tree, ready for next iteration).
Distillation status: D46c raw notes (notes/d46c-impl.md, 272 lines) + D46c-fix raw notes (notes/d46c-fix-impl.md) deleted at distillation. Build/deploy/log artifacts under /tmp/iosmux-d46c-* and /tmp/iosmux-d46c-fix-* (transient). Verdict + dyld-shared-cache finding + 2 forward paths preserved in this entry + aua-history-d38-d40.md §"D46c + D46c-fix update" + aua-side-channel-mechanism.md §"D46c + D46c-fix outcome — BLOCKED on Swift metadata accessor resolution".
Q-D66-15 — D46d (2026-05-03, Ghidra D36 query, research-only): metadata-accessor offset RESOLVED — 0x00259910; D46e c-developer dispatch ready¶
D46d was the path #2 research dispatch deriving the slice VA / file offset of _$s10CoreDevice16DeviceIdentifierOMa (Swift type metadata accessor for CoreDevice.DeviceIdentifier) for hardcoding into iosmux_aua_edi_hook.m. Approach A (Ghidra D36 project query) succeeded in 4 verification passes; havoc shared-cache extraction NOT triggered.
Resolved offset = 0x00259910 in CoreDevice.framework x86_64 slice (sha256 bea205e2c64622d144bcc7664ee104083d0e192aca206739cca345dc7c420495, matches D21/D22 baseline cited in iosmux_aua_keepalive.m:53).
Mangling subtlety (load-bearing): the canonical mangling Apple's strip pipeline preserves is _$s10CoreDevice0B10IdentifierOMa with Swift identifier-substitution compression (0B = back-reference to identifier 0). The expanded form _$s10CoreDevice16DeviceIdentifierOMa D46c tried via dlsym is literally absent from any symbol table. D46c failed for TWO simultaneous reasons: (1) shared-cache local-symbol stripping, (2) wrong mangled form. Both forms swift demangle to identical demangled string. For future Swift-runtime symbol resolution against framework internals: search for substitution-compressed form first.
Verification chain (4 Ghidra query passes, summarized): (1) exact symbol miss for expanded form + broad scan surfaced nominal type descriptor at 0x0038da0c; (2) secondary mangled labels confirmed _$s10CoreDevice0B10IdentifierOMn at 0x0038da0c; (3) xref to descriptor includes CODE ref from 0x00259923 inside Ghidra-named typeMetadataAccessor at entry 0x00259910; (4) disasm confirmed textbook Swift singleton-metadata accessor pattern (cached fast path + swift_getSingletonMetadata slow path, 33 bytes total); (5) convention check verified iosmux_aua_keepalive.m's IOSMUX_AUA_CD_REMOVECACHED_OFFSET = 0xdbe0 matches Ghidra slice VA exactly — convention established: slice VA IS the runtime offset via _dyld_get_image_header(CoreDevice) + offset.
D46e patch shape ready: #define IOSMUX_AUA_CD_DID_METADATA_OFFSET ((uintptr_t)0x00259910) + 15-line iosmux_resolve_did_metadata_accessor() mirroring iosmux_resolve_remove_cached_xpc() in inject/iosmux_aua_keepalive.m:55-93. Replace the existing dlsym/symtab walk chain in iosmux_aua_edi_hook.m's install function with a single call to the new resolver. Accessor signature: Metadata *(MetadataRequest) — RDI = request, returns {Metadata*, MetadataState} in {RAX, RDX}. Returned Metadata* yields VWT at *(metadata - 8) per existing hook logic.
Apparatus integrity: pure host-side static analysis. Zero ssh to havoc. Zero binary modifications. D36.gpr unchanged (read-only Ghidra runs). Apparatus baseline preserved (52df2cc6...4803).
Distillation status: D46d raw notes (notes/d46d-metadata-offset.md, 229 lines) deleted at distillation. Ghidra script artifacts at /tmp/iosmux_d46d_*.java + logs at /tmp/iosmux-d46d-*-out.log (transient). Verdict + offset + patch shape preserved in this entry + aua-history-d38-d40.md §"D46d update" + aua-side-channel-mechanism.md §"Path #2".
See also¶
d66-research-questions.mdd66-history-q15-d21-d33.md— Q-D66-15 D21..D33 Resolution logs.d66-history-q1-q14.md— Q-D66-1..14 Resolution logs.../research/coredevice-internals/aua-history-d36-d37.md— architectural distillation of D36..D37 era.../research/coredevice-internals/aua-history-d38-d40.md— architectural distillation of D38..D40 era.