Skip to content

AUA history — D38..D40 (2026-05-02)

Status: verified — historical archive

Distilled from D38 G-4/G-1/G-1' devicectl-side falsifications + D39 Xcode-side H#2 deploy attempt + recovery + D40 state-aware retrospective. Archived from aua-side-channel-mechanism.md on 2026-05-02 anchor refactor; the main doc retains the current architectural verdict and links here for the empirical chain.

D38 update: three devicectl-side cause-side hooks all FALSIFIED — listener Swift wrappers do not hold the send right (2026-05-02)

D38 attempted three independent devicectl-side cause-side hooks against the listener Swift class lifecycle, on the hypothesis (D37-D / D37-F) that Mercury.SystemXPCListenerConnection's deinit chain is what drops the last mach send right and triggers the kernel MACH_NOTIFY_NO_SENDERS cascade. All three failed with byte-identical Path-B outcome (errorCode 3, ~10 ms latency, no D24-era XPCError 1001 peer[2019] sentinel), on a freshly-rebooted apparatus.

Test apparatus (post-VM-reboot, 2026-05-02)

  • iosmux_inject.dylib sha 52df2cc6...4803 (D23 v2 baseline preserved)
  • CoreDevice sha bea205e2...0495 (unchanged)
  • Mercury sha 753f919e...cdbb (unchanged)
  • Tahoe 26.3.2 (25D2140) x86_64
  • iPhone (iosmux) E8A190DD-... connected (no DDI)
  • New devicectl-side dylib iosmux_devicectl_inject.dylib deployed via DYLD_INSERT_LIBRARIES wrapper at ~/iosmux/devicectl-wrapper.sh (D33 P3 feasibility re-confirmed: codesign -d shows flags=0x2000(library-validation) no runtime flag; SIP-off; ad-hoc signed dylib loads cleanly).

G-4 — extra retain on AUA-completion context at CD+0x30791 (FALSIFIED)

  • Patch: 5-byte trampoline e8 <rel32> at CD+0x30791 (the _swift_allocObject call creating lVar17 per D37-D ownership trace lines 184-217). Hook page mmap'd RWX, calls original _swift_allocObject then _swift_retain(rax) then ret. Bumps lVar17 refcount permanently.
  • Theory (per D37-F): retain lVar17 → context lives forever → listener referenced at lVar17+0x18 stays alive → no NO_SENDERS.
  • Build: dylib sha 771a9471...673154, libSystem-only, ad-hoc-signed, 32 KB.
  • Sanity: 5 install banners fired correctly in pid 1387; patch site bytes verified (e8 36 7d 31 00); rel32 reach 0xaf986a ≈ 11 MiB; install complete.
  • Empirical result: Path-B fired 9.9 ms after success() reply (unified.log 13:26:25.756664 → 13:26:25.766613). pathA_success_lines=0 / pathB_failure_lines=1 / XPCError1001_lines=0.
  • Conclusion: lVar17+0x18 is NOT a strong reference (likely raw pointer / unowned), OR a different holder releases the listener irrespective of lVar17.

G-1 — NOP listener deinit body at Mercury+0x4e930 (FALSIFIED)

  • Patch: 5-byte direct write c3 90 90 90 90 at function head Mercury.SystemXPCListenerConnection.Cfd (deinit body) at VMA 0x4e930. Verified 5 original bytes 55 48 89 e5 49. Function 40 bytes total, ending c3 at 0x4e957, 8-byte align nop pad to 0x4e960. Replacement = ret at function head, deinit body never executes its release calls.
  • Theory (per D37-D): deinit body does _swift_unknownObjectRelease(listener_xpc) → drops devicectl's last strong ref to the underlying OS_xpc_connection. Skip body → no release → no NO_SENDERS.
  • Build: dylib sha 501a6f7b...b5108, libSystem-only, 27.6 KB. Mercury found at Tahoe cryptex path /Library/Apple/System/Library/PrivateFrameworks/Mercury.framework/... (suffix-match on Mercury.framework/Versions/A/Mercury works cleanly).
  • Sanity: install banners present, patch site at runtime VMA 0x10aab9000+0x4e930 verified, 5-byte write succeeded.
  • Empirical result: Path-B fired 9.9 ms after success() reply, byte-identical surface to G-4.
  • Conclusion: D37-D's claim that 0x4e930 body does the _swift_unknownObjectRelease(listener_xpc) is FALSIFIED. Either release happens elsewhere in the deinit chain, OR the listener Swift wrapper isn't what holds the send right at all.

G-1' — NOP listener __deallocating_deinit at Mercury+0x4e960 (FALSIFIED)

  • Patch: 5-byte direct write c3 90 90 90 90 at function head Mercury.SystemXPCListenerConnection.CfD (__deallocating_deinit) at VMA 0x4e960. Verified 5 original bytes 55 48 89 e5 49 (identical prologue to body). Function does 3 helper-release calls (likely field releases via metadata) then tail-call to swift_deallocClassInstance(size=0x30, alignmask=0x7). Patch makes function ret immediately, skipping all releases AND the dealloc.
  • Theory: if release isn't in body, it's in the auto-generated __deallocating_deinit field-walker. NOP'ing the entire dealloc trampoline skips ALL field releases → Swift instance leaks forever → underlying OS_xpc_connection never released.
  • Build: dylib sha 17b2950a...4b3e2, libSystem-only, 27.6 KB.
  • Sanity: install banners present, patch at runtime 0x102a53000+0x4e960, 5-byte write succeeded.
  • Empirical result: Path-B fired 11.0 ms after success() reply (unified.log 13:58:46.278517 → 13:58:46.289534). XPCError1001_lines=0, same surface as G-4 / G-1.
  • Conclusion: skipping the entire listener dealloc trampoline did NOT prevent NO_SENDERS. The Swift class wrapper at Mercury+0x4e930-0x4ea00 range is NOT what holds the send right that, when dropped, fires the kernel notification.

Empirical conclusion across G-4 / G-1 / G-1'

Three structurally independent attempts on listener Swift wrapper lifecycle (retain context referencing listener / NOP listener body / NOP entire listener dealloc) all fail with byte-identical Path-B surface. This is significant empirical evidence that:

  • Listener Swift wrappers at Mercury+0x4e930-0x4ea00 do not hold the send right that triggers NO_SENDERS.
  • The actual holder is elsewhere — possibilities include:
    1. Peer Swift wrapper (Mercury.SystemXPCPeerConnection at Mercury+0x4e9d0-0x4ea30 — the OPPOSITE wrapper class, not listener)
    2. libxpc-internal storage held by a C-level OS_xpc_connection refcount independent of any Swift wrapper
    3. Continuation thread cleanup — the success-resume thread on devicectl side might be releasing the peer as part of async function teardown, not via listener deinit
    4. DUA._IndexBox<peer> (the late HIT #23 holder per D37-D) — holds the peer strongly, but releases only AFTER errorCode 3 has already been constructed; not the trigger

D37-D's static map is therefore INCOMPLETE — the listener-deinit chain it documented is real but is NOT on the critical path of the send right release. D37-F's G-4 alternative similarly missed.

Latency stability across attempts

Probe Hook target Latency (success → fail)
D24 baseline none ~11 ms
D30 (CDS-side AFM peer keep-alive) global g_afm_retained_peers 9.9 ms
D37-H (post-reboot baseline) none 14.7 ms
D38 G-4 CD+0x30791 trampoline 9.9 ms
D38 G-1 Mercury+0x4e930 NOP ~10 ms
D38 G-1' Mercury+0x4e960 NOP 11.0 ms

Latency does not change meaningfully across hooks → the cancel cascade is happening through a path entirely DISJOINT from our hook targets.

Implications for next research

  • Cause-side via Swift class lifecycle hooks is empirically dead end for listener wrappers. Three failure modes don't disprove it exhaustively for ALL Swift wrappers — peer wrapper untested — but the user has explicitly directed against another iteration of forcing the wrapper-class lifecycle approach: this is an undesirable path; look for the gate, not break through the wall.
  • Direction shift: instead of preventing the race that fires after GAMBIT's success() reply, look for an entirely different angle. Working hypotheses for the next probe (D39-research):
  • Hook the AUA wrapper FUNCTION ENTRY at CD+0x31750 (acquireDeviceUsageAssertion(withReason:options:) async throws). Skip the entire dispatch chain by synthesising a DeviceUsageAssertion success result directly. Bypass the race, not fight it.
  • Xcode-side direct intervention: hook DVTCoreDeviceCore's _shadowUseAssertion.fulfilled setter directly, set true without ever invoking acquireDeviceUsageAssertion. Pure UI-gate flip.
  • Real Apple flow capture: what does side-channel peer[1013] actually communicate in a working pair? Are there messages we need to send/respond-to that we're missing? D31 captured ONE success run; closer analysis of that side-channel transcript may reveal what we're not doing.

Apparatus integrity (D38 non-destructive)

  • All Apple binaries unchanged on havoc; no Apple files modified
  • iosmux_inject.dylib (CDS-side) unchanged at D23 v2 sha 52df2cc6...4803
  • New devicectl-side dylib at ~/iosmux/iosmux_devicectl_inject.dylib is opt-in via wrapper script — direct xcrun devicectl invocations are unaffected
  • No CDS / devicectl crashes; zero new DiagnosticReports
  • iPhone (iosmux) still connected (no DDI)

Distillation status

D38 raw notes preserved in repo (gitignored): notes/d38-g4-impl-spec.md, notes/d38-g4-impl-buildlog.md (with §G-4 / §G-1 / §G-1' subsections). Will be deleted post-D39 research dispatch per feedback_notes_are_temporary_buffer. Source files at inject/iosmux_devicectl_inject.m (G-1' final state, sha 03dd3275...0c2e), scripts/iosmux-devicectl-wrapper.sh, and the new Makefile target are committed and stay as the apparatus foundation for future devicectl-side experiments.

D39 update: Xcode-side hasConnection getter override — deploy

attempt + Xcode integrity incident (2026-05-02)

After D38 falsified all three devicectl-side cause-side hooks (G-4 / G-1 / G-1'), D39 research dispatch (notes file at notes/d39-gate-research.md, gitignored) ranked Hypothesis #2 as the top forward direction: hook DVTCoreDeviceCore.DVTCoreDevice_Impl.hasConnection.getter (Swift mangled _$s17DVTCoreDeviceCore0aB5_ImplC13hasConnectionSbvg) inside Xcode IDE at DVT+0x19e10, replacing the function head with 6-byte b8 01 00 00 00 c3 (mov eax, 1; ret) so the getter unconditionally returns true. Per pair-button-and-cfnetwork.md §"Pair button gating", this single Bool flip is the documented UI gate — flipping it directly bypasses the entire AUA dispatch chain that D38 cause-side hooks failed to suppress.

D39 deploy attempts

  • Attempt 1 (15:42): iosmux_xcode_inject.dylib build sha 7f81c6c9...ddb02. Sanity passed but real Xcode launch via wrapper bailed: [iosmux_d39] DVTCoreDeviceCore not in dyld image list — refusing to install. Cause: Xcode lazy-loads DVTCoreDeviceCore (only when Devices and Simulators window first opens); the constructor's one-time _dyld_image_count scan happens before lazy-load.
  • Attempt 2 (post lazy-load fix): build sha 9c76c383.... Replaced one-time scan with _dyld_register_func_for_add_image callback. Callback fires synchronously for every already-loaded image AND for each newly-loaded image, so the patch happens whenever DVTCoreDeviceCore appears regardless of lazy-vs-eager load. Sanity passed. Not yet validated against full Xcode IDE launch due to incident below.

D39 §J.6 xref pre-impl task — _shadowUseAssertion reader survey, MVP-OK verdict

D39's research spec required a pre-impl xref task to enumerate every reader of the _shadowUseAssertion ivar at field offset 0x400 on DVTCoreDevice_Impl (field-offset variable at DVT+0x6d7f8). The spec's MVP-OK precondition assumed only hasConnection.getter plus the four canonical setter-chain functions touched the field; the xref pass empirically falsified that assumption (12 callers, not 5) but the underlying RISK the spec guarded against — force-unwrap of nil shadow when our hook flips hasConnection.getter true while the ivar stays nil — does NOT materialise: every one of the 12 callers null-checks before any deref, so non-canonical readers continue down their nil branch exactly as they do today; only hasConnection.getter flips.

Twelve callers identified via r2 axt @ 0x6d7f8 on /home/op/dev/iosmux/binaries/DVTCoreDeviceCore x86_64 slice (host sha 635fbe0c1f7e0c4daed9b537976425bb96e7bb707661d51f2a8233a59d5dfabe), classified by null-check posture and UI-relevance:

# Caller VMA Containing function Null-check UI-relevance Crash risk
1 DVT+0x50a7 canRename.getter yes (test r13,r13; je) Rename button none
2 DVT+0x993a canConnectOnDemand.getter yes irrelevant for tethered iPhone none
3 DVT+0xb213 _connectAndPrepareImplicitly yes internal connect path none
4 DVT+0x14c07 deviceSummaryInternalPropertyDictionaries.getter multiple About box / diagnostic none
5 DVT+0x174ab unavailabilityErrors.getter yes possibly UI — see caveat none crash; caveat
6 DVT+0x199b5 connectedDurationMonotonicNS.getter yes uptime display none
7 DVT+0x19e5e hasConnection.getter KNOWN target Pair button gate N/A (patch site)
8 DVT+0x24429 _connectWhileLocked(reason:) yes internal lock-state path none
9 DVT+0x44a58 deinit (Cfd) release-or-skip maintenance none
10 DVT+0x44cc8 __deallocating_deinit (CfETo) release-or-skip maintenance none
11 DVT+0x490b9 init(_:) constructor WRITES *(self+0x400)=0 construction N/A (write)
12 DVT+0x4baf1 useAssertion.setter (Tf4gn_n) KNOWN canonical setter setter chain N/A

For sanity reference, the parallel _lockUseAssertion field-offset variable at DVT+0x6d7f0 has 16 callers — strict superset of shadow callers, expected because every reader takes the lock then reads shadow. Confirms the access pattern is uniform.

A separate byte-pattern search for inline 0x400 displacement returned zero hits in __text (0x16b0..0x54810) — no site reads [reg+0x400] with the offset baked in as immediate; every site goes through the field-offset variable load. This is canonical Swift-property codegen behaviour and confirms the xref pass had full coverage.

Caveats deferred to user IDE relaunch:

  1. unavailabilityErrors.getter (#5) — names suggest Xcode UI may consult this to render "device unavailable" labels. When _shadowUseAssertion is nil it returns its nil-branch result — what it does today regardless of the hook. If after the hook the device still shows as "unavailable" despite hasConnection=true, this getter is the prime suspect for an H#4 escalation.
  2. canRename.getter (#1) — Rename button in Devices window may stay disabled. Cosmetic / feature-loss only; not a crash.
  3. connectedDurationMonotonicNS.getter (#6) — uptime display may read 0. Cosmetic only.

D39 incident — Xcode integrity check tripped

After the attempt-1 launch and subsequent quit (via osascript -e 'quit app "Xcode"' for redeploy), the next user-initiated Xcode launch produced the macOS Gatekeeper alert:

Xcode can't be opened. "Xcode" is damaged and can't be opened.
Delete "Xcode" and download it again from the App Store.

Bundle on disk is presumed UNTOUCHED — H#2's mechanism is in-memory mach_vm_protect VM_PROT_COPY (per-process COW, not disk write). Sha verification post-recovery will confirm. Most likely cause: the DYLD_INSERT_LIBRARIES launch against Xcode IDE (flags=0x2000 library-validation, no hardened runtime) poisoned macOS LaunchServices / taskgated / lsd / syspolicyd cache, even though amfi_get_out_of_my_way=1 boot-arg is present and was sufficient for D33 P3's devicectl injection.

Architectural lesson

amfi_get_out_of_my_way=1 is not a complete bypass of macOS binary-protection layers when the target is Xcode-class (versus devicectl-class). D33 P3 verified DYLD_INSERT_LIBRARIES feasibility on the same boot-arg posture, but D33 tested ad-hoc-signed dylib into the comparatively-unprotected devicectl. Xcode itself goes through additional integrity layers (LaunchServices state, possibly internal codesign self-checks) that the D33 result did not validate against.

For any future Xcode-side inject work, the impl plan must include:

  1. Pre-launch sha-baseline of every Xcode binary the inject touches.
  2. Post-launch sha-verification + LaunchServices state inspection.
  3. Recovery procedure baked in (xattr clear + reboot rollback).
  4. Test the inject against a non-Xcode DVTCoreDeviceCore-loading process FIRST if any exists (e.g. xcrun simctl may load it).
  5. Don't rely on amfi_get_out_of_my_way=1 alone for Xcode-class targets — assume LaunchServices may still react.

The D39 H#2 source files (inject/iosmux_xcode_inject.m, scripts/iosmux-xcode-wrapper.sh, Makefile target) are committed and stay as the apparatus foundation for future state-tagged retest, gated on user-driven recovery confirmation.

D39 incident recovery confirmed 2026-05-02 ~16:25

User rebooted havoc, ran iosmux-restore.sh, and Xcode launches normally with synthetic iPhone (iosmux) visible in Xcode UI Devices and Simulators. The "Xcode is damaged" alert was confirmed transient LaunchServices state poisoning, not bundle corruption — bundle bytes on disk are intact.

Comprehensive sha audit 2026-05-02 16:27 confirmed every Apple binary on havoc matches the host research copy at /home/op/dev/iosmux/binaries/ byte-for-byte:

Binary sha256
CoreDevice.framework/Versions/A/CoreDevice bea205e2c64622d144bcc7664ee104083d0e192aca206739cca345dc7c420495
CoreDeviceUtilities b7ce01c47018b9bcb903fe4a134ff4b59a3db4aba69c7fcd40a6e0d4d19a7204
Mercury (cryptex) 753f919e840726f944372ab0bcc8a4c4871790312f38c98dc04f8d0978c6cdbb
devicectl 4fede2dd4fdbe542547b535f70ad320f8c33093cd23ff5b226aaad7c327cbf6c
DVTCoreDeviceCore 9c539c78210e0ab462aebbdab8e499688c8dc71c5ecf165cac50cee93e4e4d1d
Xcode IDE binary f681556c5a3d53286b50744bce3573126c3243f609125eab26db843588799b60
CoreDeviceService.xpc binary (patched with iosmux LC_LOAD_DYLIB) 9d95ad21fd81223a957ba7ef115ecbbdfb81342ec9aeb91370774e8c1ab60ac1

User-mandated comprehensive backup created at /home/op/backups/iosmux/recovery-2026-05-02/ covering all Apple bundles (binaries plus _CodeSignature/CodeResources files including the 38 MB master Gatekeeper file Xcode.app/Contents/_CodeSignature/CodeResources) plus mirrors of all five iosmux-side dylibs from havoc. Full sha manifest at recovery-2026-05-02/shas.txt.

Operational lessons distilled from the incident:

  • In-memory mach_vm_protect VM_PROT_COPY patching is genuinely per-process COW; disk bytes are not modified. Confirmed empirically by the post-recovery sha audit.
  • macOS LaunchServices / taskgated / lsd / syspolicyd may still poison process-launch state for a library-validated binary even when amfi_get_out_of_my_way=1 boot-arg is active. The poisoning is reboot-volatile.
  • Pre-launch sha baseline of every Apple bundle that the inject can conceivably reach is now mandatory before any future Xcode-side inject deploy. The recovery-2026-05-02 backup is the new baseline.

D40 update: state-aware retrospective — past 0/N baselines may be

state-conditional (2026-05-02)

After D38's three-falsification cluster the user identified that across the D20..D39 probe history, probes ran in DIFFERENT apparatus states (Xcode UI engagement, CDS lifetime, recent Pair-click history) without recording it. The long-standing inconsistency between D31's Run #2 SUCCESS path observation and the D29/D37-G/D37-H 0/N "Path-A absent" baseline likely reflects state-difference, not race jitter.

D40 retrospective mapped every probe D19..D39 to its state-tuple where determinable, classifying findings as state-invariant (~14, durable binary facts / hardcoded asm / kernel mechanisms) vs state-dependent (~8, including D31 Run #2, D29 0/N, D37-G/H baselines, D38 G-x falsifications) vs unrecoverable (~4). The forward-looking notes-file template (Dimensions 1-8 capture pre- trigger, post-trigger delta, comparison rules disallowing cross-state generalisation) lives in the state-tagged probe template runbook.

Top three historical inconsistencies plausibly explained by state difference:

  1. D31 Run #2 SUCCESS vs D29/D37-G/H 0/N — most likely lldb perturbation difference plus uncaptured Xcode UI state delta.
  2. D37-D static prediction vs D38 G-1/G-1' empirical falsification — NOT a state delta but an incomplete static-map issue; listener Swift wrappers don't actually hold the send right.
  3. D24 peer[2019] sentinel observed vs D31/D37-G "1001 sentinel gone" — apparent inconsistency is per-session pid rotation; same architectural mechanism, refer by role not pid.

State dimensions enumerated (8): Xcode UI state, CDS state, devicectl state, apparatus age, trigger type, inject configuration, probe instrumentation, ambient log baseline. Notes-file template codified in §F (Dimension 0 capture pre-trigger, Dimension Z post-trigger delta, comparison rules disallowing cross-state generalisation).

State-tagged retest plan (§E) prioritises:

  • P1: D31 Run #2 reproduction across 6-cell Xcode-state matrix.
  • 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 by D39 Xcode integrity incident; resumes post-recovery.
  • 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).

The user has explicitly assumed control of all GUI state transitions for forward retests; the dispatcher will ONLY run CLI batches on user signal "I'm in state X". No auto-launch of Xcode or auto-clicking of Devices window UI by the dispatcher.

Memory anchor feedback_state_tagged_testing.md adopted as the project-wide rule: every probe MUST record state explicitly, and findings cannot be generalised across states without empirical evidence in each.

D41 G-2 update: peer __deallocating_deinit NOP at Mercury+0x4ea10 — FALSIFIED across S1+S3 (2026-05-02)

Following D38's three-falsification listener-wrapper cluster, D41 tested the OPPOSITE Mercury Swift class — SystemXPCPeerConnection rather than SystemXPCListenerConnection — to close out D37-F's "send right holder is elsewhere" claim by exhausting the peer wrapper too. State-tagged across S1 (cold reboot, Xcode not running) AND S3 (Xcode running, Devices and Simulators open, Pair clicked) per D40 protocol. The S1+S2+S3 state-tagged retest of pre-existing baselines also ran (D40 P1 priority): naked + G-1' wrapped variants across S1, S2, S3 produced 36/36 Path-B identical to D29's historical 0/30 baseline — falsifying "past 0/N was state-conditional" hypothesis.

Hook target — peer __deallocating_deinit at Mercury+0x4ea10

Symbol: _$s7Mercury23SystemXPCPeerConnectionCfD per nm -arch x86_64. Same 5-byte patch pattern as G-1' (c3 90 90 90 90), same proven mechanism (mach_vm_protect VM_PROT_COPY in-process COW, no disk modification). Patch site verified 55 48 89 e5 49 8b 7d 20 matches listener prologue (G-1') byte-for-byte except for captured-field offset (r13+0x20 vs r13+0x10).

Mercury is FAT universal — x86_64 slice at file offset 0x4000. Required 0x4ea10 vmaddr = 0x52a10 file offset for static dd checks. First D41 agent halted on a SHA-1/SHA-256 false alarm before this became relevant; re-dispatched with shasum -a 256 directive proceeded cleanly.

Implementation

  • Source pivoted: inject/iosmux_devicectl_inject.m constant IOSMUX_D38_G1P_PATCH_VMA = 0x4e960IOSMUX_D41_G2_PATCH_VMA = 0x4ea10. Log prefix [iosmux_d38_g1p][iosmux_d41_g2]. All renames internally consistent (no leftover G-1' tokens).
  • Built on havoc: sha256 ab178ee577a1d2805c382fba9e4b39234e9aa081da5fec53aaa925a540d172a4 (G-1' was 17b2950a...4b3e2; backup at ~/backups/iosmux/d41-pre-deploy/iosmux_devicectl_inject.dylib.g1prime).
  • Deployed at /Users/nullweft/iosmux/iosmux_devicectl_inject.dylib, reachable via existing /Users/nullweft/iosmux/devicectl-wrapper.sh (wrapper script content unchanged).

State-tagged results

State C1 naked info C2 wrapped (G-2) info C3 naked list C4 wrapped (G-2) list
S1-G2 (cold, Xcode closed) 0/3 + 3/3 0/3 + 3/3 0/3 + 0/3 listed 3/3 0/3 + 0/3 listed 3/3
S3-G2 (warm Xcode + Devices&Sims + Pair clicked) 0/3 + 3/3 0/3 + 3/3 0/3 + 0/3 listed 3/3 0/3 + 0/3 listed 3/3

Hook telemetry confirmed 6/6 install banners landed in S1-G2 and 6/6 in S3-G2 (12 total wrapped invocations); zero patch-site mismatches; suffix a10 in patch-site VMA logs confirms G-2 offset (not G-1' 960).

Empirical conclusion across D38 G-4/G-1/G-1' + D41 G-2

Four structurally independent Mercury Swift wrapper hooks all FALSIFIED with byte-identical Path-B outcome (errorCode 3, ~10 ms latency, no D24-era XPCError 1001 peer[2019] sentinel):

# Probe Hook target Mercury class
1 D38 G-4 CD+0x30791 (AUA-completion ctx retain) n/a — context held listener
2 D38 G-1 Mercury+0x4e930 (deinit body NOP) SystemXPCListenerConnection
3 D38 G-1' Mercury+0x4e960 (__deallocating_deinit NOP) SystemXPCListenerConnection
4 D41 G-2 Mercury+0x4ea10 (__deallocating_deinit NOP) SystemXPCPeerConnection

State-tagged retest (D40 protocol) confirms apparatus state has zero influence: cumulative 66/66 Path-B deterministic across 6 state×inject-loadout combinations (S1+S2+S3 baselines + S1-G2+S3-G2 + 36 D29 historical). ZERO Path-A in any cell.

The send right that triggers MACH_NOTIFY_NO_SENDERS is empirically NOT held by any Swift class wrapper in Mercury reachable via DYLD_INSERT into devicectl. Holder must be in:

  • libxpc-internal C state (the underlying OS_xpc_connection refcount tracked by libxpc itself, opaque API)
  • a third Mercury class (e.g. SystemXPCConnection parent — but D37-F enumerated all Mercury deinits and ruled out cancel calls there)
  • CoreDevice-side Swift wrapper rather than Mercury-side (untested)
  • the CDS-half of the peer mach port pair, where the cancel observer fires (suppress observation rather than cause)

Implications for forward research

Cause-side via Swift wrappers in devicectl: EXHAUSTED. Six independent cause-side attempts in cumulative project history (D23 cache eviction + D30 peer lifetime + D38 G-4/G-1/G-1' + D41 G-2) all failed. The wrapper-class lifecycle approach is empirically dead-end.

Selected forward direction: Direction C via Ghidra (focused static disasm probe extending the existing D36 Ghidra project with these research questions:

  1. DUA (CoreDevice.DeviceUsageAssertion) construction site — D26's blocker. Likely inside CDS reply-decoder for AUA Output payload.
  2. Side-channel peer setup in CDS — what API CDS exposes on the peer connection, what events it posts during a successful AUA flow.
  3. Cancel-trigger upstream in CDS application code — D32 proved kernel-driven MACH_NOTIFY_NO_SENDERS, but Ghidra may find an app-code call that DROPS the send right reference, suppressible via existing CDS LC_LOAD_DYLIB patching mechanism.

Direction C results unlock Direction A (daemon-side AUA forge in CDS) as the implementation step. Direction B (H#2 persistent UI gate) remains deferred per D39 integrity incident risk class.

Apparatus integrity (D41 also non-destructive)

All Apple binaries unchanged on havoc; no Apple files modified. iosmux_inject.dylib (CDS-side) unchanged at D23 v2 sha 52df2cc6...4803. New devicectl-side dylib at ~/iosmux/iosmux_devicectl_inject.dylib (G-2 build sha ab178ee5...172a4) is opt-in via wrapper — direct xcrun devicectl invocations unaffected (verified by C1+C3 baseline producing identical results to S1/S2/S3 unmodified runs). No CDS or devicectl crashes; zero new DiagnosticReports during S1-G2 or S3-G2 batches. iPhone (iosmux) connected (no DDI) throughout. CDS PID 1535 stable across both batches.

Distillation status (D41)

D41 raw notes (notes/d41-g2-peer-wrapper.md, ~237 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Source files at inject/iosmux_devicectl_inject.m (G-2 final state). Backup of G-1' at ~/backups/iosmux/d41-pre-deploy/iosmux_devicectl_inject.dylib.g1prime (host-side, not in repo). Currently deployed dylib on havoc is G-2 — dispatcher can revert via single scp+cp from the backup if a future probe requires G-1' baseline.

D42 update: architectural inversion discovered + Q3 zero CDS-side cancel callers + DUA is NOT wire type — Direction A in original framing DEAD, Direction D (StateSnapshot probe → fix GAMBIT AFM structure) emerges (2026-05-02 evening)

State at probe: S3 (Xcode running, Devices and Simulators open, Pair clicked, "Failed to acquire assertion" error visible in UI). This is documentation discipline per feedback_state_tagged_testing.md — even pure static disasm probes record their apparatus state for later cross-correlation.

D42 was a static-only Ghidra probe extending the existing D36 project (/home/op/dev/iosmux/ghidra/) with three documentation questions about Apple's reference AUA protocol surface. First dispatch attempt 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 entire forward roadmap.

Q1 — DUA is a CLIENT-SIDE wrapper class, NOT the wire-format Codable

CoreDevice.DeviceUsageAssertion is a Swift CLASS (Hashable + Equatable, NO Codable conformance). Stored properties: 4 fields (_lockedMutableState, identifier: UUID, options: UsageAssertionOptions, reason: String) plus a computed state: State { case fulfilled / case invalidated(Error?) } that dispatches via class-method-table to read _lockedMutableState.

The wire-format Codable that flows on the side-channel peer is CoreDevice.AssertionFulfilledMessage (a struct, NOT a class):

struct CoreDevice.AssertionFulfilledMessage : Codable {
    var identifier: Foundation.UUID
    var updatedSnapshot: <SomeStateSnapshot>   // !!! gap — TMA at CD+0x29bb30
}

The DUA wrapper is built CLIENT-SIDE in CD's listener-handler block (at CD+0x2fb30) by decoding an inbound AFM. Construction site at CD+0x2ff02 calls swift_allocObject with metadata from the type- metadata accessor at CD+0xd3570, then writes 4 stored properties to runtime-patched offset globals at 0x41e8b0..0x41e8c8 and 0x3e7d80.

Implication: Direction A in its original framing ("forge a synthetic DUA in CDS") is empirically DEAD. DUA is not what flows over XPC — AFM is. GAMBIT already emits AFM today via gambit_emit_afm_via_endpoint in inject/iosmux_gambit.m.

Q2 — Architectural inversion: CDS is the CLIENT, listener is in devicectl

The side-channel peer's listener half lives in CD/devicectl, not in CDS. Per Q2 §D:

[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                  wrapped in Mercury.{System|Remote}XPCPeerConnection
                                           ├ event-handler installed via Mercury Swift API
                                           └ sends AssertionFulfilledMessage outbound

CDS receives the listener endpoint INBOUND in the action's Input.endpoint field (at action invocation time), then constructs its peer half via xpc_connection_create_from_endpoint. It wraps the raw OS_xpc_object peer into a Mercury class instance (Mercury.SystemXPCPeerConnection or Mercury.RemoteXPCPeerConnection) via swift_dynamicCastClassUnconditional. Three CFE call sites exist in CDS at 0x100020481, 0x10002120d, 0x10005e461 (the last is FetchDyldSharedCacheFiles, out of AUA scope).

Anonymous-listener provider in Mercury: Mercury.XPCSideChannel.anonymousListener() — CD/devicectl reaches this; CDS never references it.

Implication: MACH_NOTIFY_NO_SENDERS fires on the listener side (in devicectl) when the last peer-side send-right (in CDS) is dropped — confirming the D29-D40 mechanism model. The send-right holder is unambiguously inside CDS's Mercury wrapper instance.

Q2 — Side-channel protocol map (asymmetric, single-shot, no graceful termination)

Direction Message Type Frequency
CDS → CD AssertionFulfilledMessage(.success) Codable enum payload-1 exactly once per acquire
CDS → CD AssertionFulfilledMessage(.error) Codable enum payload-2 once on failure
CD → CDS (none)
Either side (no termination msg) lifecycle ends via send-right release

CD's listener handler block (CD+0x2fb30) decodes the wire enum:

  • payload 1 → AFM-success → materialize DUA (B.5 construction sequence) → Result.success(DUA) to awaiter completion
  • payload 2 → AFM-error → Result.failure(error) to awaiter
  • 1 message → warning "Received > 1 assertion fulfillment for anonymous assertion listener"

  • "Peer is unexpectedly nil" → if peer connection dropped before AFM arrives → error path

The protocol is single-shot per listener with NO graceful shutdown handshake. Lifecycle ends purely via send-right release triggered by Swift class deinit through ARC.

Q3 — ZERO CDS-side application-code cancel/release callers

Static enumeration of CDS binary (/home/op/dev/iosmux/symbols/d42-q3-cds-*.txt) found 0 direct call sites to any of the requested cancel/release/ dispose API family. The full CDS XPC API surface is 11 stubs total:

xpc_array_create, xpc_connection_create_from_endpoint (3 sites),
xpc_dictionary_create_empty, xpc_dictionary_set_{bool,string,value},
xpc_main, xpc_remote_connection_create_with_remote_service,
xpc_strerror, xpc_transaction_exit_clean,
remote_device_get_xpc_remote_connection_version_flags

CRITICALLY ABSENT: xpc_release, xpc_connection_cancel, xpc_remote_connection_cancel, xpc_connection_resume, xpc_connection_set_event_handler, xpc_connection_send_message, listener-creation APIs, dispatch_source_cancel, dispatch_mach_cancel, _xpc_connection_close, _xpc_connection_send_termination_event, _xpc_pipe_invalidate.

CDS uses pure Swift-ARC for lifecycle. Reference-counting drops → Mercury wrapper deinit fires → Mercury internally releases the OS_xpc_object → libxpc's port-rights-release path → listener side eventually receives MACH_NOTIFY_NO_SENDERS.

Updated cancel-callers matrix across the entire bundle:

Binary xpc_connection_cancel xpc_remote_connection_cancel dispatch_mach_cancel Mercury cancel thunk callers
CoreDevice 0 0 0 0
CoreDeviceUtilities 0 1 (DTRS-service-client cancel, not on AUA path) 0 0
Mercury 1 (Mercury+0x4e589, unreachable) 1 (Mercury+0x3d239, unreachable) 0 0
devicectl 0 0 0 0
CoreDeviceService 0 0 0 0

The single xpc_connection_cancel at Mercury+0x4e589 is itself unreachable from any binary (D37-F established this). The AUA peer connection is NEVER explicitly cancelled in any reachable code path. This strongly reinforces the D38+D41 conclusion that the send-right holder cannot be reached via wrapper-level Swift hooks.

Implication: CDS-side cause-side hook is empirically DEAD — there is nothing to hook in CDS application code. Any future interception must operate either below the Swift wrapper layer (libxpc / mach port APIs in Mercury/libxpc, both UNVIABLE per D33), or at the listener side (CD/devicectl's listener handler block at CD+0x2fb30) where AFM arrival/decode can be observed and substituted.

Forward design pivot — Direction D emerges

Direction A (forge DUA in CDS) is DEAD per Q1. CDS-side cause-side hook is DEAD per Q3. The wrapper-class lifecycle approach has been exhaustively falsified (D38 G-4/G-1/G-1' + D41 G-2 + D42 Q3 = 5 empirical dead-ends).

The remaining hypothesis with the lowest cost and most direct gate- finding character is:

Direction D — document StateSnapshot field set + verify GAMBIT AFM structural fidelity.

Premise: 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 referenced at CD+0x29bb30 in the listener decode chain. If the expected type is NOT DeviceStateSnapshot (most likely a different type — perhaps AssertionStateSnapshot or similar), then Apple's Codable decoder in CD's listener block fails on the type mismatch, the decode throws, and the awaiter resolves with .failure → "Failed to acquire assertion" UI label. This would be 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 and run the state-tagged batch. If AFM decode succeeds in CD listener → DUA materializes → continuation resumes with success → no race because we already won.

Apparatus integrity (D42 also non-destructive, S3 state)

  • All Apple binaries unchanged on havoc + host research copies. sha-confirmed identical to D41 baseline.
  • Ghidra project at /home/op/dev/iosmux/ghidra/ preserved (D36 + D37-F + D42 work all coexisting).
  • libxpc.dylib x86_64 slice IS available at /home/op/dev/iosmux/dsc/extracted-v3/libxpc.dylib — D37-F's libxpc gap was for arm64e only; partially closed for x86_64.
  • CoreDeviceService binary copied from recovery backup at /home/op/backups/iosmux/recovery-2026-05-02/apple-bundles/... to /home/op/dev/iosmux/binaries/ (sha-verified 9d95ad21...0ac1). NOT YET imported into Ghidra project — CDS analysis done via radare2 + llvm-objdump command-line.
  • iPhone (iosmux) connected (no DDI) throughout; CDS PID 1535 stable; zero new DiagnosticReports.
  • Apparatus state during D42: S3 (Xcode running from Dock, Devices and Simulators open, iPhone row clicked, Pair attempted, "Failed to acquire assertion" error visible in UI). Pure static disasm — no apparatus interaction during probe.

Distillation status (D42)

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 DUA class metadata, construction site, deinit chain, peer connection construction sites, CDS XPC stub inventory, full CDS disasm. Verdict + architectural inversion + Q3 zero-callers finding + Direction D pivot preserved in this section + the Q-D66-15 D42 Resolution log entry in d66-history-q15-d34-d40.md.

D43 update: TMA at CD+0x29bb30 IS DeviceStateSnapshot — type identity matches GAMBIT byte-for-byte; D42 §G working hypothesis FALSIFIED (2026-05-03, S3 state)

D43 was the focused Ghidra static probe Direction D promised — answer "what is <SomeStateSnapshot> at TMA CD+0x29bb30 and does it match what GAMBIT puts in AFM updatedSnapshot?" Answer: same type. Working hypothesis collapsed.

Type identity (resolved entirely from existing D42 dumps)

/home/op/dev/iosmux/symbols/CoreDevice-x86_64-demangled.txt:1724:

0x000000000029bb30 T type metadata accessor for CoreDevice.DeviceStateSnapshot

Same file line 5081 (AFM init demangled): init(identifier: Foundation.UUID, updatedSnapshot: CoreDevice.DeviceStateSnapshot) — confirms <SomeStateSnapshot> slot type. No new disasm needed for B.1.

Conformances (lines 1727-1729): Encodable, Equatable, Decodable. Nominal type descriptor 0x38e524. Type metadata 0x3ee8f8. Module CoreDevice (private framework).

Field set (3 stored properties = 3 CodingKeys)

D43 disassembled DSS auto-synth init(from:) at 0x29d850 and encode(to:) at 0x29d6b0. The auto-synthesized container reads/writes 3 distinct byte-tag values (0, 1, 2) for KeyedDecodingContainer keys — matches stored-property count exactly:

offset type name source
(runtime, dword[ntd+0x14]) CoreDeviceProtocols.DeviceInfo deviceInfo demangled symbols 1708..1711, designated init signature
0 [CoreDevice.Capability : [Swift.String]] capabilityImplementations demangled symbols 1717..1720, designated init at 0x29d520 writes [r12+0]
(runtime, dword[ntd+0x18]) Swift.Int64 monotonicIdentifier demangled symbols 1715..1716, designated init writes via runtime offset

Plus one computed property capabilities: Set<Capability> (getter-only, not stored, NOT in CodingKeys). NO state field on DSS.

GAMBIT comparison (mismatch analysis)

GAMBIT's gambit_build_dss_only (around inject/iosmux_gambit.m:490) emits 4 keys: state (xpc_dictionary {"connected": {}}), capabilityImplementations (empty xpc_array), deviceInfo (sub-dict), monotonicIdentifier (xpc_int64 = 1). Real CodingKeys are 3. Differences:

  • Type mismatch (DSS slot itself): NONE — TMA at CD+0x29bb30 IS CoreDevice.DeviceStateSnapshot. D42 §G working hypothesis ("real type is some <SomeStateSnapshot> different from DeviceStateSnapshot") FALSIFIED.
  • Missing fields: NONE — all 3 real CodingKeys present in GAMBIT's payload.
  • Spurious field: 1 (state) — Swift's auto-synthesized KeyedDecodingContainer<CodingKeys> only reads keys in its own CodingKeys enum. Extra keys are ignored, not validated. So state is decoder-tolerated noise, NOT decoder-poison.
  • Field-shape concern (capabilityImplementations): GAMBIT emits empty xpc_array_create(NULL, 0). Real type is Dictionary<Capability, [String]> where Capability is non-String- keyed (Swift struct). Empty container shape is ambiguous. Recorded as "may be acceptable, needs falsification" — D44 closes this.

Apparatus integrity (D43 non-destructive)

Static disasm only. No apparatus interaction. iosmux_inject.dylib sha unchanged (52df2cc6...4803); CoreDevice / Mercury / CDU SHA unchanged; iPhone (iosmux) connected (no DDI); CDS PID stable.

Distillation status (D43)

Raw notes (notes/d43-statesnapshot-probe.md, 235 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Disasm artifacts preserved at /home/op/dev/iosmux/symbols/d43-dss-*.txt (host-side, not in repo). Verdict + field set + mismatch analysis preserved in this section + the Q-D66-15 D43 Resolution log entry in d66-history-q15-d34-d40.md.

D44a update: capabilityImplementations target shape = xpc_array-of-pairs; Capability is auto-synth Codable struct in CDU with 2 String CodingKeys (2026-05-03, research)

D44a was the cheap follow-up research probe to D43's outstanding field-shape concern. Goal: determine the exact xpc-Codable shape Apple's auto-synth init(from:) decoder expects for capabilityImplementations: [Capability:[String]] and produce a ready-to-paste C/xpc-API recipe for D44b's empirical falsifier.

Top-level shape: xpc_array-of-pairs

Disasm of DSS encode(to:) at 0x29d6b0 shows the conformance witness selected at 0x29dba0 is SDyxq_GSEsSERzSER_rlMc — the Swift stdlib's UNCONSTRAINED-key Dictionary<x, q>: Encodable where x: Encodable, q: Encodable. NOT the keyed-by-string variant (SDyxq_GSEsSERzs0sB12RepresentableR_rlMc).

Cross-check: Capability's conformance witness table in CDU (symtab lines 701-707) lists VSEAAMc, VSHAAMc, VSLAAMc, VSQAAMc, VSeAAMc, Vs12IdentifiableAAMc, Vs23CustomStringConvertibleAAMcno StringProtocol, no CodingKeyRepresentable conformance. Runtime cannot promote Capability into the keyed-by-string path, must use UnkeyedContainer with alternating [K0, V0, K1, V1, ...].

Encoder + decoder agree → wire shape is xpc_array of alternating Capability dicts and [String] arrays.

Capability identity: 2-String auto-synth Codable struct

Module: defined and exported by CoreDeviceUtilities (CDU); CoreDevice imports via chained-fixup relocs. Bare nominal / metadata-accessor symbols _$s10CoreDevice10CapabilityVMa (0x165af0) and ...VMn (0x002695c4) live in CDU.

Stored properties (per field-descriptor cstring run at CDU+0x0026df50):

  1. name: Swift.String
  2. featureIdentifier: Swift.String

CodingKeys (auto-synth, by stored-property name): name (tag 0), featureIdentifier (tag 1). Confirmed via direct disasm of Capability.encode(to:) at CDU+0x1654b0 and init(from:) at CDU+0x165650 — exact KeyedEncodingContainer.encode<String>(_:forKey:) sequences with the 2 byte tags.

Wire form: xpc_dictionary { "name": <xpc_string>, "featureIdentifier": <xpc_string> }.

Apparatus integrity (D44a non-destructive)

Static disasm only. iosmux_inject.dylib sha unchanged (52df2cc6...4803); Apple binaries unchanged; iPhone state preserved.

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, not in repo). Recipe + shape determination preserved in this section + the Q-D66-15 D44a Resolution log entry.

D44b update: env-gate via launchctl setenv does NOT propagate to CoreDeviceService XPC service post-killall on Tahoe 26.x — apparatus learning (2026-05-03)

D44b was the first attempt at the field-shape falsifier. Built D44a's recipe under env-gate IOSMUX_D44_CAPIMPL=1 so the new path could be toggled at runtime without rebuild. Patch compiled / linked / codesigned clean. Build sha new from D23 v2. CDS respawned via killall.

Failure mode: env-gate never reached CDS

Agent set env via ssh havoc-root launchctl setenv IOSMUX_D44_CAPIMPL 1 BEFORE killall, then triggered xcrun devicectl device info details. Verified empirically via launchctl procinfo on the respawned CDS PID — IOSMUX_* was absent from the env vector. Apple-allowed XPC service vars were present; iosmux ones were not.

The env-gated branch in gambit_build_dss_only therefore never executed ((no D44b lines) in inject log). The unified-log result is BY CONSTRUCTION identical to the empty-array baseline: single line Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3), no Codable / typeMismatch / Capability decoder errors anywhere. Falsifier never ran. No empirical signal.

Cause: NOT SIP

User explicitly 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 some other Tahoe 26.x-specific launchd behavior. No further investigation done; symptom-only is sufficient apparatus learning.

Apparatus integrity (D44b non-destructive)

D23 v2 baseline restored cleanly via backup at /home/op/backups/iosmux/d44b-pre-deploy/iosmux_inject.dylib.d23v2-presnapshot. Post-restore sha 52df2cc6...4803 verified. iPhone (iosmux) connected (no DDI) post. Working tree clean. 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. Captured artifacts at /tmp/iosmux-d44b-*.{log,txt} host-side (transient, lost on host reboot).

D44b' update: capabilityImplementations field-shape FALSIFIED — Direction D dead, reframe required (2026-05-03, unconditional retest)

D44b' re-ran D44b's falsifier with the env-gate removed — the new non-empty capabilityImplementations path runs unconditionally on every gambit_build_dss_only call. Mandatory restore via backup at end of probe limits exposure window.

Test apparatus (S1 cold)

User-confirmed pre-trigger: Xcode NOT running, VM uptime 1:15, CDS PID rotates 1961 → 2143 → 2222 across deploy cycles. iPhone (iosmux) connected (no DDI). Pre-trigger inject sha = D23 v2 baseline. New build sha a02fb802f1f4454d7bc01ce14d877cf9053a67982ff473f52c11112c417b2f03 (203424 bytes, 1 pre-existing warning unrelated to D44, codesign OK).

Inject log confirms new path executed

/home/op/backups/iosmux/d44b-pre-deploy/iosmux_inject.log.d44b-prime lines 687-690:

687: [inject] === PoC registration complete. Device visible in Xcode. ===
688: [inject] GAMBIT: synthesised reply for aid=com.apple.coredevice.action.acquireusageassertion ...
689: [inject] GAMBIT: D44b unconditional capImpl entry emitted
690: [inject] GAMBIT: emitted AFM endpoint=0x7ff1e3822c20 did=E8A190DD-...

The xpc_array sent on the wire contained one (Capability, [String]) pair with name="iosmux.test.cap1", featureIdentifier="iosmux.test.cap1.id", single string "impl1". D44b's apparatus gap is RESOLVED.

Unified log: ZERO Codable decoder errors

Verbatim grep of 60s window (/tmp/iosmux-d44b-prime-unified-log.txt, 29 lines) 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 Codable decoder errors anywhere.

Critical sequencing (lines 22-29)

22 11:32:06.598085 [action] Invoking AcquireDeviceUsageAssertion ... invocation=171380B5...
23 11:32:06.598847 [action] forward(...) Forwarding to ExecutionLocation.coreDeviceService.
24 11:32:06.599503 [action] Forwarding to ExecutionLocation.elsewhere(<SystemXPCPeerConnection ... pid = 2222 ...>).
25 11:32:06.602825 [action] Received reply from forwarded action: SUCCESS()
26 11:32:06.602851 [action] Received reply from action: SUCCESS()
27 11:32:06.605777 [analytics] Core Analytics ...
28 11:32:06.613866 [analytics] Reporting to CA ...
29 11:32:06.615570 [useassertion] Failed to acquire usage assertion ... CoreDeviceError(errorCode: 3, ...)

The xpc-Codable decoder accepted the GAMBIT reply (lines 25-26 success()). The error fires ~13 ms LATER on the useassertion subsystem (devicectl-side), NOT the action / decoder subsystem. This is post-decode logic that consumes the DSS payload and decides "no assertion was acquired" — the failure is on a code path that runs AFTER the auto-synth init(from:) decoder.

Verdict — Direction D dead

D43 §Recommendations option (1) FALSIFIED. The post-decode logic is NOT inspecting capabilityImplementations for non-empty (D44b' would have flipped the outcome; it did not). AUA Path-B determinism does NOT live on the capabilityImplementations shape. Two surviving candidate surfaces remain (per Forward direction in aua-side-channel-mechanism.md):

(a) different DSS field consumed post-decode by useassertion subsystem (e.g. DeviceInfo sub-field invariant, monotonicIdentifier value-check); OR

(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).

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 CDS / devicectl DiagnosticReports. iPhone post-restore connected (no DDI). git status clean for inject/iosmux_gambit.m.

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 section + the Q-D66-15 D44b' Resolution log entry.

D45 update: D30 hypothesis #1 FALSIFIED + Q4 architectural pivot — useassertion subsystem lives in CoreDeviceService, not in CD/devicectl (2026-05-03, Ghidra static, research-only)

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 cannot know about. Verdict: no such UUID exists on the AUA endpoint side-channel path, plus a Q4 architectural pivot that reshapes the surviving candidate surface from devicectl-side to CDS-side.

Q1 — AUA wrapper UUID construction sites

Confirmed Tahoe 26.x offsets:

  • 0x317a0 — body of CoreDevice.RemoteDevice.acquireDeviceUsageAssertion(withReason:options:) async throws -> CoreDevice.DeviceUsageAssertion, entered via swift_task_switch from prologue at 0x31750. Sets up withCheckedThrowingContinuation, allocates a Swift continuation buffer, calls func.000304a0.
  • 0x304a0 — completion-handler-flavoured AUA entry. Reads RemoteDevice.deviceIdentifier from field.class.CoreDevice.RemoteDevice.var.deviceIdentifier (offset global at 0x41e8c8), bridges Strings, builds an AcquireDeviceUsageAssertionActionDeclaration carrier, dispatches the action via 0x3472d8.

Neither function calls uuid_generate_random / CFUUIDCreate / Foundation.UUID.init(). Verified by exhaustive inspection of all call instructions in /home/op/dev/iosmux/symbols/d45-aua-async-body-clean.txt and /home/op/dev/iosmux/symbols/d42-q1b-cb-acquire.txt — only swift_* runtime, objc_* retain/release, NSProgress KVO bridge, and the action-dispatch funnel. No UUID constructor of any flavour. No per-call UUID minted client-side before the action goes on the wire.

Q1 — DUA construction at AFM-decode site (CD+0x2fb30..0x2ff3d)

Decisive structure (D42 disasm dump /home/op/dev/iosmux/symbols/d42-q1-fn-2fb30.txt):

0x2fc54  call swift_getEnumCaseMultiPayload(decoded_AFM, type_metadata)
0x2fc62  cmp eax, 1
0x2fc64  jne 0x2fde9          ; case 0 = .success(AFM)
0x2fde9  (case 0 entry — AFM-success payload)
   0x2fdfb cmp byte [rcx + 8], 0   ; first arrival check
   0x2fec5 je   0x300bc             ; peer-pointer nil → "Peer is unexpectedly nil"
0x2fee4  mov byte [rcx + 8], 1
0x2fee8..0x2feff
   AFM[+0x10] -> var_30h            ; UUID lo64
   AFM[+0x18] -> var_28h            ; UUID hi64 (UUID is 16 bytes)
   AFM[+0x20] -> r15                ; updatedSnapshot (DSS reference)
0x2ff02  call CD+0xd3570             ; type metadata accessor for DUA
0x2ff11  call swift_allocObject       ; r14 = new DUA instance
0x2ff19  lea rax, field.class.CoreDevice.DeviceUsageAssertion.var.identifier
                                     ; runtime-patched offset global at 0x41e8b0
0x2ff23  mov rbx, [rax]
0x2ff26  add rbx, r14                 ; rbx = &dua->identifier
0x2ff2b  call 0x347110                ; UUID type metadata accessor
0x2ff30  mov rcx, [rax - 8]            ; UUID value-witness table
0x2ff34  mov rdi, rbx                  ; dest = &dua->identifier
0x2ff37  mov rsi, r13                  ; src = AFM identifier slot pointer
0x2ff3d  call [rcx + 0x10]             ; vwt[2] = initializeWithCopy(dest, src)
                                       ;          COPY 16-byte UUID into DUA

There is NO equality compare against any pre-stored UUID at the AFM-decode site. The UUID is whatever the AFM payload carried — copied byte-for-byte into the DUA. A hypothetical client-side comparison if afm.identifier != expected.identifier { fail } would be visible as a swift_stdlib_memcmp / explicit cmp 16-byte check before the DUA construct. None exists between 0x2fc54 and 0x2ff3d.

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 — a UUID-keyed dictionary that is the kind of side-state hypothesis #1 predicted.

But IAD is NOT on the AUA endpoint side-channel path:

  • CoreDevice.TunnelAssertionRequest.identifiable(IdentifiableAssertionDetails, () -> ()) is one case of the TunnelAssertionRequest enum (the other is .persistent). The request enters via TunnelAssertionGroup.add(pending: TunnelAssertionRequest) at CD+0x290f30tunnel-management API, not the Mercury action-invocation surface AUA uses.
  • AUA action declaration's Input has exactly 3 fieldsreason: 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 IAD reference. The AUA action invocation envelope does NOT carry any per-assertion UUID.
  • Pattern scan for direct call rel32 to CD+0x290330 returned 0 hits in CD's text segment — the IAD constructor is reached only via Swift PWT dispatch, on a code path that is parallel to (not part of) the AUA endpoint side-channel.

Q3 — reachability from GAMBIT

AUA invocation envelope has no per-assertion UUID. Top-level keys are the standard 6 documented in mercury-envelope-empirical.md §"Pair action invocation envelope" (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. The DUA gets whichever value was sent; the downstream gate that fires errorCode 3 is unrelated to this field.

Direction E (assertion-instance UUID mismatch) is therefore not a "one-line GAMBIT swap" fix and is not "architecturally unreachable" either — it simply does not exist as a mechanism. GAMBIT current behaviour is correct for what the AFM-decode site does with the identifier.

Q4 PIVOT — useassertion subsystem lives ONLY in CoreDeviceService

grep -ac useassertion enumeration across the bundle (artifact at /tmp/claude-1000/.../tasks/{bv92g4hhs,b117pzx95}.output):

Binary hits
CoreDevice 0
CoreDeviceUtilities 0
Mercury 0
devicectl 0
CoreDeviceService >0 (subsystem string com.apple.dt.coredevice.useassertion. 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" candidate (b) — the surviving candidate is CDS-side.

CDS-side strong candidate strings on the AUA failure path:

CDS+offset String
0xbbd30 "Cannot acquire a usage assertion on a device without an effective device identifier." — strongest gate candidate (post-success()-reply check, presumably the format string for the errorCode-3-throwing site's NSLocalizedFailureReasonErrorKey)
0xbc030 "This device does not support acquiring a usage assertion." — capability gate
0xbbef0 "Acquired usage assertion." — success log line
0xc2e40 format: "Received request to create usage assertion %{public}s for device %{public}s, (required preparedness=%ld, owning pid=%d, reason=%s)" — server-side per-request log; assertion-id is generated server-side at request receipt, never echoed back to the client
0xc3160 format: "Notifying client of invalidation for usage assertion %{public}s, device %{public}s. Error: %s"
0xc3aa0 "Received unexpected message through usage assertion acquisition control channel: %s"
0xc3b00 format: "Usage assertion %{public}s was invalidated by device %{public}s with error: %s"
0xbfb20 "The tunnel was disconnected because no outstanding usage assertions were held."

CDS-side handler symbols (in /home/op/dev/iosmux/symbols/d45-cds-demangled-x86_64.txt):

  • L2113 0x1000b6404CoreDeviceService.AcquireDeviceUsageAssertionActionImplementation nominal type descriptor (mangled prefix _$s17CoreDeviceService07AcquireB34UsageAssertionActionImplementationV).
  • L2117 0x1000b641e — associated conformance to CoreDeviceUtilities.ActionImplementation-style protocol — confirms this is a CoreDeviceUtilities-action-system handler running in CDS, not in devicectl.
  • L2114 0x1000b640aCoreDeviceService.ListUsageAssertionsActionImplementation (companion handler).
  • L2100 0x1000b6386CoreDeviceService.InProgressServerAssertion Obj-C class (__DATA at 0x1000d2e68, IVARS at 0x1000d2048) — the server-side per-assertion-bookkeeping object. The assertion-id from the format string at 0xc2e40 is presumably its identifier.
  • L2417 0x1000b7324CoreDeviceService.RemotePairingDeviceRepresentation conforms to CoreDevice.UseAssertionProvidingDeviceRepresentation (the protocol with acquireUsageAssertion(forRequest:), releaseAssertion(identifiedBy: UUID), listAllAssertions()).

Tooling gap surfaced for D46

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) and to CD+0x290330 (IAD.init) returned 0 hits via direct call rel32 / lea rip+rel byte patterns. D46 needs Ghidra (D36 project at /home/op/dev/iosmux/ghidra/D36.gpr already imports CDS) or llvm-objdump --chained-fixups CoreDeviceService 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.

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 prep checklist (next probe scope)

  1. Find direct callers of 0xbbd30 ("Cannot acquire a usage assertion on a device without an effective device identifier.") in CDS via Ghidra D36 project (chained-fixup-aware xref).
  2. Confirm whether that string is the direct format consumed by the errorCode 3 log site, or whether the wrapping CoreDeviceError(errorCode: 3, ...) is constructed elsewhere and the string is only the user-facing NSLocalizedFailureReasonErrorKey.
  3. Inspect CoreDeviceService.AcquireDeviceUsageAssertionActionImplementation's run/execute body (anonymous in nm output — Ghidra address findable from the protocol-conformance descriptor at 0x1000b641e) and trace the post-success()-reply path that leads to the useassertion log emission.
  4. If candidate (1) is the firing site, characterise what "effective device identifier" means semantically on the CDS side — presumably a lookup keyed on the device record retrieved from RSDDeviceWrapper, post-Pair. This is a different semantic axis from D30 hypothesis #1 entirely, and may surface a concrete inject-side fix target on the CDS side (where iosmux already has load-bearing interpose chains).

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 per feedback_notes_are_temporary_buffer. 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 section + the Q-D66-15 D45 Resolution log entry + aua-side-channel-mechanism.md §"Cumulative falsifications" + §"Forward direction".

D46 update: Direction F CONFIRMED + bridgeable — predicate is _get_effectiveDeviceIdentifier getter in CoreDevice.framework (2026-05-03, Ghidra static probe)

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 ~13ms 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.

Q1 — Direct callers of the AUA-fail-EDI string

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; the x86_64 slice starts at FAT offset 0x4000, so subtract 0x4000 to convert FAT → slice. The D45 0xb7d90 subsystem string and 0xc2e40 log format were already slice-relative and remain correct.

Direct xref count to 0x1000b7d30 = 0 for Swift-rendered error strings. D46 Q1 explained why: the Swift compiler packs __cstring densely and constructs the error message at runtime via Swift String length-prefixed encoding from a SHARED SUFFIX 0x1000b89e0 ("an effective device identifier."). The 8 LEAs into the suffix appear across 4 sibling gates (DDI-disable, DDI-enable, Pair, Tunnel-create) plus the AUA path itself.

LEA hit map (artifact: /tmp/iosmux-d46-q1g-rangerefs.txt):

code-VA enclosing function LEA target
10002eb15 FUN_10002e4b0 (DDI-disable) 0x1000b89e0 shared suffix
10002ef6e FUN_10002e4b0 (DDI-disable) same
10002f1ea FUN_10002e4b0 (DDI-disable) same
10002fea6 FUN_10002f950 (DDI-enable) same
100030124 FUN_10002f950 (DDI-enable) same
100030697 FUN_10002f950 (DDI-enable) same
1000309dc FUN_10002f950 (DDI-enable) same
100030f90 FUN_100030cf0 (DDI helper) same
100040351 FUN_1000386d0 (Pair/Tunnel umbrella) 0x1000b9200 sibling

D46 used the #file literal LEAs to identify which function is the AUA-specific run body:

code-VA function #file literal
10001e92f FUN_10001e120 CoreDeviceService/AcquireDeviceUsageAssertionActionImplementation.swift
1000202d4 FUN_10001fa00 "uiring a usage assertion." (separate capability gate)
10002157f FUN_100020700 "e device identifier." (AUA-specific tail)

The 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 (action dispatcher), identifies FUN_100020700 as the AUA action run body.

Q2 — Firing function + predicate

Firing function: FUN_100020700 at slice VA 0x100020700, size 0x11fc bytes.

Gate predicate (decompile citation: /tmp/iosmux-d46-q2-funcsearch.txt:3266-3354):

// 1. extract UUID from inbound action invocation
CoreDeviceUtilities::Context::get_deviceIdentifier(...);

// 2. look up CDS-internal SDR by UUID
___swift_instantiateConcreteTypeFromMangledNameV2(&DAT_1000d3590, &DAT_1000b3040);
CoreDevice::CoreDeviceService::_serviceDeviceRepresentation(<UUID>, conformingTo);

// 3. GATE — read Optional<DeviceIdentifier>
CoreDevice::ServiceDeviceRepresentation::_get_effectiveDeviceIdentifier(this, out);

// 4. enum-witness "isCase(.none)"
iVar14 = (*opt_witness_isCase_none)(...);

if (iVar14 == 1) {  // .none → FAILURE PATH
    CoreDevice::CoreDeviceError::get_deviceRepresentationMissingIdentity(...);
    Swift::Error::_init(...);
    _swift_allocError(...);
    CoreDeviceUtilities::_Continuation::_resume(error);  // throws errorCode 3
    _swift_errorRelease(...);
    return;   // <-- this is errorCode 3 emitted ~13ms after success() reply
}

// gate passed → extract Input fields → dispatch to FUN_10001fa00 (AFM emit)

Predicate semantics in plain English:

let effectiveDeviceID: Optional<DeviceIdentifier> =
    serviceDeviceRepresentation._effectiveDeviceIdentifier

if effectiveDeviceID == nil {
    throw CoreDeviceError.deviceRepresentationMissingIdentity
}

Cited code-site addresses (slice VAs in CDS):

  • 0x100020700FUN_100020700 AUA action run body.
  • 0x100020cff — predicate call site.
  • 0x100020d5d_get_deviceRepresentationMissingIdentity invocation.
  • 0x100020df3_Continuation::_resume(error) (throw point).
  • 0x100018ea0/100018ea5 — action router → AUA-body dispatch.

Q3 — "Effective device identifier" semantics

ServiceDeviceRepresentation._effectiveDeviceIdentifier: Optional<DeviceIdentifier> is a Swift property on the CDS-internal SDR populated post-RSD-pair. Distinct from the inbound Context.deviceIdentifier UUID and from the 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.
  • (b, less likely) explicit invalidation marker is set.

iosmux's existing inject chain (MDRemoteServiceSupport, iosmux_md_proxy.m, serviceDeviceRepresentations interpose) supplies device data into CD-side / devicectl-side structures but does NOT touch the CDS-side SDR's _effectiveDeviceIdentifier field. This is a separate state surface that iosmux has not yet touched — the architectural advantage D45 Q4 hinted at.

The conformance-witness chain is: CoreDeviceService.RemotePairingDeviceRepresentationCoreDevice.UseAssertionProvidingDeviceRepresentationCoreDevice._RSDDeviceWrapperConvertibleCoreDevice.ServiceDeviceRepresentation. The _get_effectiveDeviceIdentifier getter is EXTERNAL to CDS — defined in CoreDevice.framework, called via the protocol-witness table.

Q4 — Full AUA action implementation body

FUN_100020700 body skeleton (~4.6 KB, source citation: /tmp/iosmux-d46-q2-funcsearch.txt:2853-3911):

void FUN_100020700(undefined8 param_1, Context *param_2, undefined8 param_3) {
    // Stage 1 — type metadata accessor instantiation (~20 calls)
    CoreDevice::CoreDeviceError::typeMetadataAccessor();
    CoreDevice::IdentifiableAssertionDetails::typeMetadataAccessor();
    Mercury::Attributes::typeMetadataAccessor();
    Dispatch::DispatchQoS::typeMetadataAccessor();
    OS_dispatch_queue::AutoreleaseFrequency::typeMetadataAccessor();
    CoreDevice::UsageAssertionInformation::typeMetadataAccessor();
    // ...

    // Stage 2 — extract Context::deviceIdentifier
    CoreDeviceUtilities::Context::get_deviceIdentifier(...);

    // Stage 3 — SDR lookup by UUID
    CoreDevice::CoreDeviceService::_serviceDeviceRepresentation((UUID), conformingTo);

    // Stage 4 — GATE
    CoreDevice::ServiceDeviceRepresentation::_get_effectiveDeviceIdentifier(this, out);
    if (Optional.isCase(.none)) {
        // throw CoreDeviceError.deviceRepresentationMissingIdentity → errorCode 3
        return;
    }

    // Stage 5 — extract Input fields (gate passed)
    CoreDevice::AcquireDeviceUsageAssertionActionDeclaration::Input::get_options(...);
    CoreDevice::AcquireDeviceUsageAssertionActionDeclaration::Input::get_reason(...);
    CoreDevice::AcquireDeviceUsageAssertionActionDeclaration::Input::_get_endpoint(...);

    // Stage 6 — dispatch to AFM-emit happy path
    // (FUN_10001fa00: builds InProgressServerAssertion, opens side-channel
    //  via xpc_connection_create_from_endpoint, emits AFM via Mercury)
}

The 13 ms gap between D44b' line 25 success() (synchronous routing reply from the forward(...) infrastructure) and line 29 errorCode 3 is exactly the time FUN_100020700 spends doing Stages 1-4 above plus the error construction + continuation throw at the gate trip.

Q5 — InProgressServerAssertion lifecycle correlation

The Obj-C class at CDS+0x1000b6386 is the server-side per-assertion-bookkeeping object. It is allocated in FUN_10001fa00 AFTER the gate at Stage 4 passes — it does NOT exist at gate-trip time. Hooking IPSA's construction or its IVAR getters does NOT help; the gate is upstream of any IPSA allocation.

IVAR layout (at 0x1000d2048, base 0x1000d2e68): assertionIdentifier/peerConnection/deviceIdentifier/ serviceDeviceRepresentation/requiredPreparedness/assertionDetails/ invalidationHandler/invalidated/queue/devicePowerAssertion at offsets +0x3420..+0x3468.

FUN_10001ad60 is the invalidation handler (calls CoreDevice::UseAssertionProvidingDeviceRepresentation::releaseAssertion(UUID) + acquires os_unfair_lock on devicePowerAssertion + 0x18). Out of D46 scope; documented for D46b implementation reference.

Q6 — D46b implementation roadmap

Recommended hook target #1: _get_effectiveDeviceIdentifier getter (EXTERNAL CoreDevice.framework symbol).

Property Value
Reachability dlsym(RTLD_DEFAULT, "_$s10CoreDevice32ServiceDeviceRepresentationP012_effectiveB10IdentifierAA0B10IdentifierVSgvg") (mangled candidate; D46b prep verifies exact mangling)
Calling convention Swift class-method ABI; this in rdi, indirect-result buffer in rsi. C-compatible: void hook(void *out, SDR *this)
Hook payload call original; if out tag byte = 1 (.none), copy <context.deviceIdentifier> UUID into out, set tag = 0 (.some)
Trampoline S1.B pattern proven at inject/iosmux_inject.m:1463-1525
Hook recipe pseudocode call original; if (*((uint8_t *)out + 16)) == 1
Failure surface If next-layer consumer can't find real SDR for fabricated UUID, downstream lookup may fail differently — moves gate-trip downstream, gives diagnostic visibility
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

Hook target #2 (FUN_100020700 prologue replacement) and #3 (_get_deviceRepresentationMissingIdentity constructor hook) evaluated and rejected as more brittle.

D46b prep gaps (research-only dispatch needed before D46c impl):

  1. Exact mangled symbol via swift demangle over nm CoreDevice.
  2. Disassembly of getter prologue (need ≥13 clean linear bytes).
  3. Confirm getter is not fully inlined under -O3.
  4. Verify exact Optional<DeviceIdentifier> ABI shape (assumed 16+1 bytes; needs disassembly verification of FUN_100020700 at the predicate-load site 0x100020cff).

After D46b prep closes, D46c is the implementation dispatch.

Architectural advantage — bridgeable from existing inject

_get_effectiveDeviceIdentifier is in CoreDevice.framework. CDS imports CoreDevice via LC_LOAD_DYLIB — and so does our existing iosmux_inject.dylib. Same address space. The getter is reachable via standard dlsym(RTLD_DEFAULT, ...) from inside the inject. No new injection target required — the same mechanism that's already proven for the S1.B trampoline pattern works here.

Tooling stack (D46)

  • 8 Java GhidraScripts authored (PyGhidra mode required Java port — Jython removed in Ghidra 12).
  • Ghidra D36 project at /home/op/dev/iosmux/ghidra/D36.gpr 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.

Why this finally found the gate after 8 falsifications

All prior probes (D23..D45) operated on CD-side / devicectl-side / Mercury-side surfaces because the failure presentation ([useassertion] Failed to acquire ... errorCode 3 arriving at the awaiter) suggested a client-observable mechanism. D45 Q4 inverted that assumption empirically (subsystem string only in CDS), and D46 followed the trail into the CDS-internal action implementation where the predicate actually lives. The gate is neither a connection lifetime issue (D23..D42 Q3), nor a Codable shape issue (D43..D44b'), nor a UUID-comparison issue (D45 D30 #1) — it is server-side device-record state validation that iosmux has never touched.

Apparatus integrity (D46 non-destructive)

Pure host-side static disasm; zero apparatus interaction. inject sha unchanged at 52df2cc6...4803. iPhone untouched. CDS unchanged. Apple binaries unchanged. Working tree clean post-distill.

Distillation status (D46)

Raw notes (notes/d46-cds-useassertion-trace.md, ~430 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Ghidra-script artifacts under /tmp/iosmux-d46-*.{sh,java,py,txt} (transient; lost on host reboot). Full empirical decompile at /tmp/iosmux-d46-q2-funcsearch.txt (8344 lines, transient). Empirical findings + D46b prep gaps + recommended hook target preserved in this section + the Q-D66-15 D46 Resolution log entry + aua-side-channel-mechanism.md §"Forward direction — Direction F CONFIRMED + bridgeable post-D46".

D46b update: D46c READY-WITH-CAVEATS — ¾ prep gaps closed; D46 hook recipe overridden via capture-and-replay (2026-05-03, Ghidra static probe, research-only)

D46b was the prep dispatch closing the 4 verification gaps D46 surfaced. Goal: confirm exact mangled symbol of _get_effectiveDeviceIdentifier, byte-level prologue disassembly, inline status under -O3, and exact Optional<DeviceIdentifier> ABI shape — so D46c implementation can be specified without remaining unknowns. Verdict: ¾ RESOLVED + 1 PARTIAL (with empirical fallback); several CRITICAL CORRECTIONS to D46's plan.

Q1 RESOLVED — exact mangled symbol

_$s10CoreDevice07ServiceB14RepresentationC09effectiveB10IdentifierAA0bF0OSgvgTj

Demangled (via swift demangle):

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:

Component D46 guess D46b reality
Type kind P (protocol) C (Swift class)
DeviceIdentifier kind V (struct) O (Swift enum, per D45 §Q1.3)
Property name _effectiveDeviceIdentifier (leading underscore) effectiveDeviceIdentifier (no underscore)
Symbol form bare getter vg dispatch thunk vgTj (only externally exported)

Three errors in a single guessed symbol — none would have linked at dlsym. The bare vg getter is NOT externally exported (verified via llvm-nm -a /home/op/dev/iosmux/binaries/CoreDevice listing all 6 effective-related symbols). Only the Tj dispatch thunk is externally callable.

CDS imports the thunk by name: chained-fixup table entry name_offset = 29559 (_$s10CoreDevice07ServiceB14RepresentationC09effectiveB10IdentifierAA0bF0OSgvgTj) at /tmp/iosmux-d46b-q1-cds-fixups-grep.txt:5. CDS's call site at 0x100020cff resolves at load time to the thunk's address in the loaded CoreDevice image.

Address: thunk at slice VA 0x28db10 in CoreDevice x86_64 slice.

Q2 RESOLVED — prologue disasm + trampoline viability

Thunk prologue at 0x28db10:

0x28db10  [ 1] 55                       PUSH RBP                                 cumulative 1
0x28db11  [ 3] 48 89 e5                 MOV  RBP, RSP                            cumulative 4
0x28db14  [ 4] 49 8b 4d 00              MOV  RCX, [R13]          ; R13 = self    cumulative 8
0x28db18  [ 7] 48 8b 89 d8 00 00 00     MOV  RCX, [RCX + 0xd8]   ; vtable slot   cumulative 15
0x28db1f  [ 1] 5d                       POP  RBP                                 cumulative 16
0x28db20  [ 2] ff e1                    JMP  RCX                 ; tail call     cumulative 18

15 contiguous clean linear bytes before the JMP RCX terminator. No PC-rel, no PAC, no branch, no return. Sufficient headroom for a 12-byte absolute-jump trampoline (MOV RAX, imm64 (10 bytes) + JMP RAX (2 bytes) = 12). S1.B trampoline pattern at inject/iosmux_inject.m:1463-1525 is viable with no modification.

Critical caveat: the thunk is a tail-call dispatcher, not the real implementation. Hooking the thunk replaces the entire vtable indirection — when our hook is called, we must implement the entire getter contract (including avoiding infinite recursion with the trampoline-saved-bytes patched-back original).

Sample bare getter at 0x28a440 (selfReportedDeviceIdentifier): prologue contains PC-relative ADD at offset 7 + LEA at offset 14 — only ~6 clean bytes before first PC-rel. Bare getters are NOT trampoline-safe; thunks ARE. Empirical confirmation that hook target must be the thunk, not the bare getter.

Q3 RESOLVED — getter callable via thunk

Bare getter (concrete vtable-slot implementation) is NOT externally exported. However, the dispatch thunk at 0x28db10 IS exported (T text symbol), externally linkable, imported by CDS, called from CDS via CALL 0x1000b0a56 (CDS-internal stub → thunk in CoreDevice) at CDS:0x100020cfa.

Inlining of the bare getter is irrelevant for D46c — the thunk is the canonical externally-callable dispatch entry. Even if Apple's optimizer inlined some callers' invocations of the bare getter internally to CoreDevice, the thunk symbol still exists for any cross-image consumer (like CDS).

Witness-table alternative NOT NEEDED: D46 contemplated a "conformance-witness-table slot" alternative if the getter was inlined. This is moot — ServiceDeviceRepresentation is a Swift class (mangling C), not a protocol (P). Class method dispatch goes through a vtable, not a witness table. The thunk IS the vtable-dispatch entry; no separate witness slot exists.

Q4 PARTIAL — Optional uses VWT runtime tag access (NOT fixed byte layout)

The naive D46 hook recipe if (out[16] == 1) { memcpy(out, uuid, 16); out[16] = 0; } will NOT work. D46 assumed a fixed "16+1" layout. Empirical disasm at CDS:0x100020d20:

0x100020d07  MOV  RDI, R15                    ; arg 0 = Optional buffer ptr
0x100020d0a  MOV  ESI, 0x1                    ; arg 1 = numEmptyCases = 1
0x100020d16  MOV  RDX, RBX                    ; arg 2 = DeviceIdentifier type metadata ptr
0x100020d19  MOV  R12, [RBP + -0x138]         ; R12 = VWT pointer
0x100020d20  CALL [R12 + 0x30]                ; VWT slot +0x30 = getEnumTagSinglePayload
0x100020d25  CMP  EAX, 0x1                    ; if EAX == 1 → .none
0x100020d2c  JNZ  0x100020e1e                 ; not-none → success branch
                                              ; fall-through → error/throw branch

Swift uses runtime VWT call to getEnumTagSinglePayload (slot +0x30) for Optional tag inspection. There is no fixed byte-tag offset. The actual byte layout depends on DeviceIdentifier's spare-bits flags which cannot be statically determined without disassembling its nominal type descriptor + VWT contents.

getEnumTagSinglePayload returns:

  • 0 → .some(<payload>) — payload bytes valid at offset 0
  • 1 → .none — first (and only) empty case for Optional

Confirmed by CDS's CMP EAX, 0x1; JNZ <not-none-branch>.

DeviceIdentifier payload sizing (from D45 §Q1.3): 2-case enum — case ecid(Swift.UInt64) (8 bytes) and case uuid(Foundation.UUID, Swift.String) (~32 bytes). Multi-payload enum layout: max-payload + case-tag. Optional packing on top adds either spare-bits encoding (no size increase) or appended tag (+1-8 bytes). Static disasm cannot definitively determine which.

Stack-frame buffer at [RBP + -0xd0] in FUN_100020700 — exact size not directly visible from snapshot.

D46c must use either:

  1. Capture-and-replay (preferred — layout-agnostic): capture a known-good .some buffer at runtime from a sibling gate's natural success, replay on .none via VWT destroy + initializeWithCopy. Avoids tag-byte writes entirely.
  2. VWT-mediated tag manipulation: call getEnumTagSinglePayload to read tag, storeEnumTagSinglePayload to write — heavier and requires constructing a valid DeviceIdentifier payload from scratch (which needs the .uuid case-constructor mangled symbol).

D46c hook payload is documented in aua-side-channel-mechanism.md §"Hook payload — capture-and-replay strategy".

Calling convention (Swift class-method getter on x86_64) — non-cdecl

Per the thunk disasm at 0x28db10, the calling convention is:

  • R13 = self (SDR class instance pointer; thunk reads [R13] to load class metadata, then [metadata + 0xd8] for the bare getter address).
  • RAX = indirect-result buffer pointer for Optional<DeviceIdentifier> return value.

This is NOT standard SysV AMD64 cdecl. D46 had assumed this in rdi and indirect-result in rsi — this is also wrong.

D46c hook entry MUST use inline-asm preamble to capture R13 and RAX into C-accessible variables before any C code runs:

void *self_ptr;
void *out_buf;
__asm__ volatile (
    "mov %%r13, %0\n"
    "mov %%rax, %1\n"
    : "=r" (self_ptr), "=r" (out_buf)
    :
    : "memory"
);

Updated risk register (D46 R1 + new D46b R5-R11)

Risk ID Description D46 status D46b status
R1 Hook fires for ALL 4 sibling gate paths plus AUA OPEN OPEN — concrete mitigation drafted (TODO-D return-address discriminator); ~10 lines C
R2 Mangled symbol guess wrong OPEN RESOLVED — exact thunk symbol confirmed
R3 Prologue not trampoline-friendly OPEN RESOLVED — 15 contiguous clean bytes
R4 Getter inlined under -O3 OPEN RESOLVED — bare getter not exported but thunk is
R5 (NEW) Optional uses Swift VWT for tag inspection — no fixed byte layout n/a OPEN — capture-and-replay sidesteps
R6 (NEW) Non-cdecl calling convention (R13=self, RAX=indirect-result) n/a OPEN — inline-asm preamble at hook entry
R7 (NEW) Need known-good .some buffer to bootstrap capture-and-replay n/a OPEN — capture from first natural .some on sibling gate
R8 (NEW) Hook recurses if buffer construction triggers another effectiveDeviceIdentifier call n/a OPEN — per-thread in_hook flag
R9 (NEW) dlsym(RTLD_DEFAULT, ) may behave unexpectedly n/a OPEN — fallback via dyld_get_image* exports trie walk
R10 (NEW) Constructing DeviceIdentifier from scratch needs case-constructor mangled symbol n/a OPEN — capture-and-replay obviates the need
R11 (NEW) selfReportedDeviceIdentifier sibling could be backup target if effective fails n/a OPEN — documented; primary remains effective thunk

D46c implementation dispatch — open TODOs

TODO Description
TODO-A Capture-and-replay primary path (capture .some from sibling gate, replay on AUA)
TODO-B dlsym(RTLD_DEFAULT, mangled) fallback if Swift mangled symbol resolution fails
TODO-C Resolve Optional<DeviceIdentifier> size at runtime via swift_getGenericMetadata
TODO-D Sibling-gate return-address discriminator (FUN_100020700 range hardcoded)
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. Working tree clean post-distill.

Distillation status (D46b)

Raw notes (notes/d46b-prep-gaps.md, 915 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Ghidra script artifacts at /tmp/iosmux-d46b-*.{sh,java,txt,log} (transient). Verdict + corrections to D46 + D46c TODOs preserved in this section + the Q-D66-15 D46b Resolution log entry + aua-side-channel-mechanism.md §"D46b-resolved hook target" + §"Hook payload — capture-and-replay strategy".

D46c + D46c-fix update: hook code authored + build/deploy clean; BLOCKED on Swift metadata-accessor resolution; 2 paths forward (2026-05-03)

D46c was the c-developer implementation dispatch — D46c-fix the delta dispatch closing one of the two metadata-accessor resolution paths empirically. Both ran without apparatus regressions; both landed at a SECONDARY block that revealed an architectural fact about Tahoe 26.x's dyld shared cache.

D46c outcome (clean build/deploy, install bails)

D46c authored:

  • inject/iosmux_aua_edi_hook.h — public API iosmux_aua_edi_hook_install(void).
  • inject/iosmux_aua_edi_hook.m (~433 lines) — naked-asm outer wrapper preserves Swift cc R13 (self) + RAX (indirect-result)
  • callee-saved regs across CALL into trampoline; C body inspects the Optional tag via Swift VWT slot +0x30, captures .some byte image on first observation, replays via VWT destroy (slot +0x08) + initializeWithCopy (slot +0x10) when AUA-caller path produces .none. Per-thread recursion guard via __thread. AUA caller-discriminator computed from CDS image base + hardcoded FUN_100020700 range (CDS:0x100020700..0x1000218fc).
  • inject/iosmux_inject.m — added #include + final iosmux_aua_edi_hook_install() call at end of iosmux_inject_init.
  • inject/Makefile — added iosmux_aua_edi_hook.m to source list.

Build chain on havoc: make clean + make succeeded; build sha 7b196be83ca56206357bff13a08c2678cfa602cb3aa10a92dfe5d22a24e80d0a (first deploy) and 439421936acdba224305d7a62b80e43b48bd679976e5b48fca612c0dd3954df1 (after scoped-dlsym fallback). Codesign clean. Deploy via ssh havoc-root cp to /Library/Developer/CoreDevice/iosmux_inject.dylib clean. CDS respawn (PID 8086 first, 8198 second) clean.

Devicectl trigger ran end-to-end: xcrun devicectl device info details --device E8A190DD-... exit 0 (WARNING Failed to acquire assertion still fires because hook never armed).

Empirical block at install function:

  • D46c: thunk resolved at 0x10d5e0b10 — thunk dlsym OK.
  • D46c: dlsym(RTLD_DEFAULT, $s10CoreDevice16DeviceIdentifierOMa) returned NULL — metadata accessor NOT in exports trie.
  • D46c-fix added scoped dlsym(dlopen("/Library/Developer/PrivateFrameworks/CoreDevice.framework/CoreDevice", RTLD_NOLOAD), ...): also returned NULL.
  • D46c: install FAILED — dlsym($s10CoreDevice16DeviceIdentifierOMa) returned NULL.

Hook entry log D46c: hook installed at thunk 0x... NEVER fires because install bails before trampoline patching. Trampoline never patched. Thunk never hooked. AUA gate continues to trip.

CDS PID stable across both deploys. Zero new DiagnosticReports. iPhone state stable at connected (no DDI). Apparatus restored to D23 v2 baseline at end (52df2cc6...4803).

D46c-fix outcome (LC_SYMTAB walk also blank)

D46c-fix authored a Mach-O LC_SYMTAB walker as fallback in iosmux_aua_edi_hook.m:

  • find_coredevice_image_index() — iterates _dyld_image_count() / _dyld_get_image_name(i), exact trailing-component match /CoreDevice (avoids "CoreDeviceUtilities").
  • resolve_via_symtab(target) — locates LC_SYMTAB load command in the chosen image, maps __LINKEDIT segment via LC_SEGMENT_64 to convert file offsets to in-memory pointers, iterates nlist_64 entries (skipping N_STAB), matches symbol name against target.

Built clean. Deployed clean. Build sha 0f5482afd9dfa99e3abcee9075b7aefcfa67fc537188230fff08d1db5a2e52be.

Empirical result:

D46c: thunk resolved at 0x...
D46c: dlsym(RTLD_DEFAULT, $s10CoreDevice16DeviceIdentifierOMa) returned NULL — trying scoped dlsym
D46c: scoped dlsym also returned NULL — trying LC_SYMTAB walk
D46c: resolve_via_symtab — image[5] header=0x11146c000 slide=0x11146c000 linkedit_mem=0x111498000 nsyms=5281 stroff=4611680 symoff=4512448
D46c: resolve_via_symtab — '_$s10CoreDevice16DeviceIdentifierOMa' not found in 5281 symbols
D46c: install FAILED — symtab walk also failed for metadata accessor

5281 nlist entries iterated correctly. Zero matches for the target symbol. Per-image LC_SYMTAB is also blank for this internal Swift symbol.

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 shipped with the cache binary. Both dlsym (exports trie) AND direct nlist_64 walk per-image return blank for internal Swift symbols (e.g., type metadata accessors _$s..OMa for non-public types).

Captured in memory rule feedback_dyld_shared_cache_strips_symbols. Affects any future symbol-resolution attempt against framework internals — skip these two paths and go directly to Ghidra-hardcoded offsets OR dynamic metadata recovery.

CDS PID stable through D46c-fix (PID 8563 alive post-trigger). Zero new DiagnosticReports. iPhone stable. Apparatus restored.

Forward paths (post-D46c-fix)

Two paths remain to provide the metadata accessor; existing hook code activates with one-line install-function patch once either 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-purpose agent): Ghidra/llvm-objdump on the on-disk extracted CoreDevice binary (extracted via dyld_shared_cache_util since shared-cache strips don't apply to the on-disk artifact) → derive slice VA of _$s10CoreDevice16DeviceIdentifierOMa.
  • D46e (impl, c-developer agent): replace install function's dlsym/symtab resolution with g_did_meta = ((MaFn)((uint8_t *)cd_base + OFFSET))(); build, deploy, validate (this time hook should arm).

Path #3 — Dynamic metadata recovery via Swift runtime. Single c-developer dispatch authoring runtime metadata walker: hook captures R13 (self) on first entry, walks *self → metadata pointer → vtable → ... to recover Optional<DeviceIdentifier>'s metadata indirectly. Version-resilient (no hardcoded offsets); higher implementation risk (Swift runtime ABI undocumented for non-public types). 50-100 lines of Swift-runtime-aware C.

Path #2 recommended (project precedent, lower risk, faster to empirical validation).

Apparatus integrity (D46c + D46c-fix non-destructive overall)

D23 v2 baseline preserved through both dispatches. Pre-deploy backup verified pre-deploy + post-restore. Zero CDS crashes. Zero new DiagnosticReports. iPhone connected throughout. Working tree clean post-distill (this commit).

Distillation status (D46c + D46c-fix)

Raw notes (notes/d46c-impl.md + notes/d46c-fix-impl.md, combined ~600 lines) deleted at distillation time per feedback_notes_are_temporary_buffer. Build/deploy/validation artifacts under /tmp/iosmux-d46c-*.{txt,sh} and /tmp/iosmux-d46c-fix-*.{txt,sh} (transient). Hook code authored in inject sources is committed to the repo separately (commit pending). Empirical findings + dyld-shared-cache learning + 2 forward paths preserved in this section + the Q-D66-15 D46c Resolution log entry + aua-side-channel-mechanism.md §"D46c + D46c-fix outcome".

D46d update: metadata-accessor offset RESOLVED via Ghidra D36 query — 0x00259910 for D46e c-developer patch (2026-05-03)

D46d was the path #2 research dispatch deriving the slice VA / file offset of _$s10CoreDevice16DeviceIdentifierOMa (the 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 was NOT triggered.

Resolved address

Slice VA / image-base-relative offset = 0x00259910 in CoreDevice.framework x86_64 slice (sha256 bea205e2c64622d144bcc7664ee104083d0e192aca206739cca345dc7c420495, matches D21/D22 baseline cited in iosmux_aua_keepalive.m:53 — same binary, no drift).

Mangling subtlety (load-bearing for future sessions)

The expanded form _$s10CoreDevice16DeviceIdentifierOMa D46c tried via dlsym is literally absent from Ghidra's symbol table and also from any nm output. The canonical mangling Apple's strip pipeline preserves is _$s10CoreDevice0B10IdentifierOMa — identifier-substitution-compressed (0B = back-reference to identifier index 0). Both forms swift demangle to the same demangled string type metadata accessor for CoreDevice.DeviceIdentifier.

This means D46c's failure had TWO causes simultaneously:

  1. Shared-cache local-symbol stripping (memory rule feedback_dyld_shared_cache_strips_symbols — D46c-fix's nlist walk would have failed regardless).
  2. Wrong mangled form in the dlsym call — even an unstripped binary wouldn't match the expanded form.

For any future Swift-runtime symbol resolution against framework internals: search for the SUBSTITUTION-COMPRESSED form first, not the length-prefixed expanded form. swift demangle on the canonical form confirms semantic identity.

Verification chain (4-pass Ghidra)

  1. Query #1 (iosmux_d46d_query.java) — exact-symbol miss for expanded form; broad scan surfaced $$type_metadata_for_CoreDevice.DeviceIdentifier at 0x003ecf80, CoreDevice::DeviceIdentifier at 0x0038da0c.
  2. Query #2 (iosmux_d46d_query2.java) — secondary mangled labels confirmed: 0x003ecf80 = _$s10CoreDevice0B10IdentifierON (type metadata DATA), 0x0038da0c = _$s10CoreDevice0B10IdentifierOMn (nominal type descriptor). Both demangle as expected.
  3. Query #3 (iosmux_d46d_xref.java) — xrefs to the Mn descriptor at 0x0038da0c include CODE ref from 0x00259923 (a LEA RSI, [0x38da0c] inside Ghidra-named function typeMetadataAccessor entry 0x00259910).
  4. Query #4 (iosmux_d46d_disasm.java) — disasm of 0x00259910 confirmed textbook Swift singleton-metadata accessor pattern (cached fast path + swift_getSingletonMetadata slow path via LEA RSI, [Mn]; CALL stub). Function is 33 bytes. Secondary label _$s10CoreDevice0B10IdentifierOMa attached at the entry point.
  5. Query #5 (iosmux_d46d_verify.java) — convention check: verified that iosmux_aua_keepalive.m's IOSMUX_AUA_CD_REMOVECACHED_OFFSET = 0xdbe0 matches Ghidra's slice VA for removeCachedXPCConnection exactly. Convention established: slice VA in this project IS the offset to use at runtime via _dyld_get_image_header(CoreDevice) + offset.

D46e patch shape (ready)

// inject/iosmux_aua_edi_hook.m additions:

#define IOSMUX_AUA_CD_DID_METADATA_OFFSET ((uintptr_t)0x00259910)

static void *iosmux_resolve_did_metadata_accessor(void) {
    uint32_t n = _dyld_image_count();
    for (uint32_t i = 0; i < n; i++) {
        const char *name = _dyld_get_image_name(i);
        if (!name) continue;
        size_t len = strlen(name);
        const char *suffix = "/CoreDevice.framework/Versions/A/CoreDevice";
        size_t slen = strlen(suffix);
        if (len < slen) continue;
        if (strcmp(name + len - slen, suffix) != 0) continue;
        const struct mach_header *hdr = _dyld_get_image_header(i);
        return (void *)((uintptr_t)hdr + IOSMUX_AUA_CD_DID_METADATA_OFFSET);
    }
    return NULL;
}

Replace the existing dlsym/symtab walk chain in the install function with a single call to this resolver. The accessor's signature is Metadata *(MetadataRequest)RDI = request, returns {Metadata*, MetadataState} in {RAX, RDX}. Caller-side, cast to the appropriate Swift ABI shape and invoke; the returned Metadata* then yields the VWT at *(metadata - 8) per existing hook logic.

Apparatus integrity (D46d non-destructive)

Pure host-side static analysis on the Linux host. Zero apparatus interaction. Zero ssh to havoc. Zero binary modifications. Read-only Ghidra runs (-noanalysis -readOnly) — D36.gpr unchanged.

Distillation status (D46d)

Raw notes (notes/d46d-metadata-offset.md, 229 lines) deleted at distillation per feedback_notes_are_temporary_buffer. Ghidra script artifacts under /tmp/iosmux_d46d_*.java and logs at /tmp/iosmux-d46d-*-out.log (transient). Verdict + offset + patch shape preserved in this section + the Q-D66-15 D46d Resolution log entry + aua-side-channel-mechanism.md §"Path #2".

See also