Files
vphone-cli/research/boot_jb_mount_failure_investigation.md
T
Lakr 5388e0c9c5 Squash merge startup-hang-fix into main
Prefix research patch comparison doc and normalize root markdown names

Rename research root markdown files to scoped topic names
2026-03-06 02:42:12 +08:00

8.3 KiB

JB Mount Failure Investigation (2026-03-04)

Symptom

  • make setup_machine JB=1 reached cfw_install_jb and failed at:
    • Failed to mount /dev/disk1s1 at /mnt1 (opts=rw).

Runtime Evidence (Normal Boot)

From make boot serial log:

  • APFS mount tasks fail with permission errors:
    • mount_apfs: volume could not be mounted: Operation not permitted
    • mount: /private/xarts failed with 77
    • mount: /private/preboot failed with 77
    • launchd panics: boot task failure: mount-phase-1 - exited due to exit(77)
  • Ignition/boot path shows entitlement-like failure:
    • handle_get_dev_by_role:13101: disk1s1 This operation needs entitlement

This indicates failure in APFS role-based device lookup during early boot mount tasks.

Runtime Evidence (DEV Control Run, 2026-03-04)

From a separate fw_patch_dev + cfw_install_dev boot log (not JB):

  • mount-phase-1 succeeded for xART:
    • disk1s3 mount-complete volume xART
    • /dev/disk1s3 on /private/xarts ...
  • launch progressed to:
    • data-protection
    • finish-obliteration
    • detect-installed-roots
    • mount-phase-2

Interpretation: APFS boot-mount path can work on this build/kernel family after recent APFS gate changes. This does not prove JB flow is fixed; it is a control signal showing the kernel-side path is not universally broken.

Flow Separation (Critical)

  • The successful xART mount-complete / mount-phase-2 log is from DEV pipeline:
    • fw_patch_dev + cfw_install_dev
  • JB pipeline remains:
    • fw_patch_jb + cfw_install_jb
  • cfw_install_jb does not call cfw_install_dev; it runs base cfw_install.sh first, then JB-only phases.

Kernel Artifact Checks

1) Ramdisk kernel identity

  • vm/Ramdisk/krnl.img4 payload hash was byte-identical to:
    • vm/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vphone600

So ramdisk boot was using the same restore kernel payload (no accidental file mismatch in ramdisk_build).

2) Patchability state (current VM kernel)

On vm/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vphone600:

  • Base APFS patches:
    • patch_apfs_vfsop_mount_cmp -> not patchable (already applied)
    • patch_apfs_mount_upgrade_checks -> not patchable (already applied)
  • Key JB patches:
    • patch_mac_mount -> patchable
    • patch_dounmount -> patchable
    • patch_kcall10 -> patchable

Interpretation: kernel is base-patched, but critical JB mount/syscall extensions are still missing.

3) Reference hash comparison

  • CloudOS source kernelcache.research.vphone600 payload:
    • b6846048f3a60eab5f360fcc0f3dcb5198aa0476c86fb06eb42f6267cdbfcae0
  • VM restore kernel payload:
    • b0523ff40c8a08626549a33d89520cca616672121e762450c654f963f65536a0

So restore kernel is modified vs source, but not fully JB-complete.

IDA Deep-Dive (APFS mount-phase-1 path)

1) Failing function identified

  • APFS function: sub_FFFFFE000948EB10 (log name: handle_get_dev_by_role)
  • Trigger string in function:
    • "%s:%d: %s This operation needs entitlement\\n" (line 13101)
  • Caller xref:
    • sub_FFFFFE000947CFE4 dispatches to sub_FFFFFE000948EB10

2) Gate logic at failure site

The deny path is reached if either check fails:

  • Context gate:
    • BL sub_FFFFFE0007CCB994
    • CBZ X0, deny
  • "Entitlement" gate (APFS role lookup privilege gate):
    • ADRL X1, "com.apple.apfs.get-dev-by-role"
    • BL sub_FFFFFE000940CFC8
    • CBZ W0, deny
  • Secondary role-path gate (role == 2 volume-group path):
    • BL sub_FFFFFE000817C240
    • CBZ W0, deny (to line 13115 block)

The deny block logs line 13101 and returns failure.

3) Patch sites (current vphone600 kernelcache)

  • File offsets:
    • 0x0248AB50 — context gate branch (CBZ X0, deny)
    • 0x0248AB64 — role-lookup privilege gate (CBZ W0, deny)
    • 0x0248AC24 — secondary role==2 deny branch (CBZ W0, deny)
  • All three patched to NOP in the additive APFS patch.

4) Additional APFS EPERM(1) return paths in apfs_vfsop_mount

Function:

  • sub_FFFFFE0009478848 (apfs_vfsop_mount)

Observed EPERM-relevant deny blocks:

  • Root-mount privilege deny:
    • log string: "%s:%d: not allowed to mount as root\n"
    • xref site: 0xFFFFFE000947905C
    • error return: sets W25 = 1
  • Verification-mount privilege deny:
    • log string: "%s:%d: not allowed to do a verification mount of %s (is_suser %s ; uid %d)\n"
    • xref site: 0xFFFFFE0009479CA0
    • error return: sets W25 = 1

Important relation to existing Patch 13:

  • At 0xFFFFFE0009479044 (same function), current code is CMP X0, X0 (patched form), which forces the following B.EQ path and should bypass one root privilege check in this region.
  • Therefore, if JB still reports mount_apfs ... Operation not permitted, remaining EPERM candidates include other deny branches (including the verification-mount gate path above), not only handle_get_dev_by_role.

Root Cause (Updated, Two-Stage)

Stage 1 (confirmed and mitigated):

  • APFS handle_get_dev_by_role entitlement/role deny gates were a concrete mount-phase-1 blocker.
  • Additive patch now NOPs all three relevant deny branches.

Stage 2 (still under investigation, JB-only):

  • DEV control run can pass mount-phase-1/mount-phase-2.
  • JB failures must be analyzed with JB-only artifacts/logs and likely involve JB-only deltas (launchd dylib injection, BaseBin hooks, or JB preboot/bootstrap interaction), in addition to any remaining kernel checks.

Mitigation Implemented

A) Ramdisk kernel split (updated implementation)

  • scripts/fw_patch_jb.py
    • no longer creates a ramdisk snapshot file
  • scripts/ramdisk_build.py
    • derives ramdisk kernel source internally:
      • uses legacy kernelcache.research.vphone600.ramdisk if present
      • otherwise derives from pristine CloudOS kernelcache.research.vphone600 under ipsws/*CloudOS*/ using base KernelPatcher
    • builds:
      • Ramdisk/krnl.ramdisk.img4 from derived/base source
      • Ramdisk/krnl.img4 from post-JB restore kernel
  • scripts/ramdisk_send.sh
    • prefers krnl.ramdisk.img4 when present.

B) Additive APFS boot-mount gate bypass (new)

  • Added new base kernel patch method:
    • KernelPatchApfsMountMixin.patch_apfs_get_dev_by_role_entitlement()
  • Added to base kernel patch sequence in scripts/patchers/kernel.py.
  • Behavior:
    • NOPs three deny branches in handle_get_dev_by_role
    • does not modify existing filesystem patches (APFS snapshot/seal/graft/mount/sandbox hooks remain unchanged).

C) JB-only differential identified (for next isolation)

Compared with DEV flow, JB adds unique early-boot risk factors:

  • launchd binary gets LC_LOAD_DYLIB injection for /cores/launchdhook.dylib
  • launchdhook.dylib/BaseBin environment strings include:
    • JB_ROOT_PATH
    • JB_TWEAKLOADER_PATH
    • explicit launchdhook startup logs (hello / bye)
  • procursus/bootstrap content is written under preboot hash path (/mnt5/<hash>/jb-vphone)

These do not prove causality yet, but they are the primary JB-only candidates after Stage-1 APFS gate mitigation.

Next Validation

  1. Kernel/JB isolation run (requested):
    • make fw_patch_jb
    • make ramdisk_build
    • make ramdisk_send
    • run cfw_install_dev (not JB) on this JB-patched firmware baseline
  2. Compare normal boot result:
    • If mount-phase-1/2 succeeds: strong evidence issue is in JB-only userspace phases.
    • If it still fails with EPERM: continue kernel/APFS deny-path tracing.
  3. If step 2 succeeds, add back JB phases incrementally:
    • first JB-1 (launchd inject + jetsam patch)
    • then JB-2 (preboot bootstrap)
    • then JB-3 (BaseBin hooks) and capture first regression point.

2026-03-05 Follow-up (Data-Protection / SEP UserClient MACF)

A later failure mode moved past mount-phase and failed in data-protection:

  • IOUC AppleSEPUserClient failed MACF ... seputil
  • Boot task failed: data-protection - exited due to exit(60)

This was traced to unpatched IOKit MAC policy hook range (ops[201..210]) in the sandbox extended hook set. Mitigation and patch details are documented in:

  • research/boot_data_protection_seputil_macf_investigation.md

Follow-up (2026-03-06):

  • Even after ops[201..210] extension, runtime still showed:
    • IOUC AppleAPFSUserClient failed MACF ...
    • IOUC AppleSEPUserClient failed MACF ...
  • A second-stage mitigation was added:
    • patch_iouc_failed_macf (central IOUC MACF gate low-risk early return).