Summary
Path B has two separate problems that are currently presented as one bypass flow:
- The reported step-2 error is caused by an internal USB handle/state lifecycle bug.
- Even after that bug is fixed, the current Path B implementation does not provide a functional A12+ activation path.
The first problem is directly reproducible with the existing code and explains #42:
[INFO] [path_b] Step 1/10: Rebooting device from DFU to recovery mode...
[INFO] [path_b] DFU abort sent, waiting for recovery mode...
[INFO] [path_b] Recovery mode detected (30s)
[INFO] [path_b] Step 2/10: Manipulating in recovery mode...
[ERROR] [path_b_id] No USB handle for DFU mode
This report is focused on the root cause and possible solutions. It complements #42, #88, #91, #82, and #38 rather than replacing those user reports.
Affected Target
- Device: iPhone XS Max
- SoC: A12 Bionic
- CPID:
0x8020
- Firmware: iOS
18.7.9
- Path:
path_b_a12plus
Reproduction
- Put an A12+ device into DFU mode.
- Run the normal flow or
./tr4mpass --force-path-b.
- Allow Path B to complete the DFU-to-recovery transition.
- Observe that recovery is detected, followed immediately by the no-handle error.
The failure does not depend on the USB serial descriptor index. It occurs before the serial descriptor read is attempted.
Expected Behavior
After the DFU handle is closed and recovery mode is detected, Path B should either:
- use a recovery-mode
libirecovery client for all subsequent recovery operations, or
- explicitly reacquire and store a recovery handle before invoking the identity step.
It must not require a DFU-only libusb_device_handle after the device has intentionally left DFU mode.
Actual Control Flow
1. DFU handle acquisition
src/main.c:130-151 calls usb_dfu_find(), reads CPID/ECID, and stores the opened DFU handle in dev->usb.
src/device/usb_dfu.c:83-122 opens Apple DFU VID 0x05AC / PID 0x1227, detaches the kernel driver, and claims interface 0.
2. DFU-to-recovery transition
src/bypass/path_b.c:88-153 sends DFU_ABORT, closes the handle, and explicitly clears the state:
usb_dfu_close(dev->usb);
dev->usb = NULL;
dev->is_dfu_mode = 0;
The function then creates a temporary libusb context and polls for recovery PID 0x1281.
3. Step 2 rejects the intentional state
src/bypass/path_b.c:161-174 immediately calls path_b_manipulate_identity() after recovery is detected.
src/bypass/path_b_identity.c:331-339 still requires dev->usb to be non-NULL:
if (!dev->usb) {
log_error("[path_b_id] No USB handle for DFU mode");
return -1;
}
That guard is incompatible with the previous function, which intentionally closed and nulled the DFU handle.
Why This Is Not Primarily a Descriptor-Index Bug
The current tree already contains the runtime descriptor-index work that an older implementation was missing:
src/device/usb_dfu.c:97-102 captures bDeviceDescriptor.iSerialNumber.
src/device/usb_dfu.c:204-215 probes the runtime hint followed by legacy indices 3 and 4.
src/bypass/path_b_identity.c:118-194 uses the cached index with the same fallbacks.
src/exploit/checkm8_spray.c:191-201 passes the runtime index into checkm8 helpers.
A descriptor-index failure would result in a CPID/ECID parsing or serial-read error. The observed error is emitted at path_b_identity.c:337, before path_b_read_serial() is reached.
There are still transition issues worth fixing:
src/bypass/path_b.c:104-106 ignores the return value of the raw DFU_ABORT transfer.
- The recovery poll only accepts PID
0x1281, while path_b_identity.c:267-276 accepts recovery modes corresponding to 0x1280..0x1283.
- The recovery poll does not match the detected ECID, so another connected recovery device could be mistaken for the target.
Existing Recovery-Side Code
The repository already has code that appears intended to handle the post-transition state:
src/bypass/path_b_identity.c:197-227 reads the serial through libirecovery when dev->usb == NULL.
src/bypass/path_b_identity.c:231-305 opens recovery mode and uses irecv_setenv() / irecv_saveenv().
src/bypass/path_b.c:184-249 later reopens recovery mode for the reboot-to-normal step.
The stale DFU-only precondition makes this recovery branch unreachable from the real Path B orchestration.
Test Coverage Gap
tests/integration/test_path_b_e2e.c:19-21 explicitly avoids executing path_b_module.execute() because the real transition polls can take up to 60 seconds.
The identity test at tests/integration/test_path_b_e2e.c:82-128 injects a fake non-NULL USB handle. It therefore cannot catch the production sequence where step_reboot_to_recovery() sets dev->usb = NULL.
A full mocked state-machine test is needed with these states:
DFU handle present
-> DFU_ABORT
-> DFU handle closed
-> recovery detected
-> dev->usb == NULL
-> libirecovery serial read/write
-> recovery reboot
-> normal-mode lockdownd reconnect
Compatibility Finding: A12+ Support Is Not Functional
The repository's chip table correctly marks CPID 0x8020 as A12 and non-checkm8-vulnerable:
include/device/chip_db_table_rop.h:175-190
tests/test_chip_db.c:37-42
src/exploit/checkm8.c:28-43
However, Path B accepts any non-checkm8 device:
src/bypass/path_b.c:52-77
The wrapper also labels every nonzero CPID as supported:
The README claims functional Path B support for A12 through A17:
README.md:6-8
README.md:68-78
README.md:193-199
The current Path B implementation does not contain an A12 SecureROM exploit. It changes an iBoot environment value and then attempts to use locally fabricated session data. The activation record intentionally contains empty SEP-bound fields:
src/activation/record.c:157-162
src/activation/record.c:218-231
The offline session code also states that the locally fabricated response requires a patched mobileactivationd:
src/activation/session.c:78-83
src/activation/session.c:281-297
Path B never performs the Path A jailbreak/mobileactivationd replacement. The integration test documents the expected A12+ activation rejection rather than a successful activation:
tests/integration/test_path_b_e2e.c:132-187
Therefore, removing the stale handle guard would fix the immediate error but would not make iPhone XS Max / A12 / iOS 18.7.9 activation functional.
Possible Solutions
Option 1: Minimal state/lifecycle fix
Make the existing recovery implementation reachable and correct:
- Remove the DFU-only
dev->usb precondition from path_b_manipulate_identity().
- Add an explicit device-mode enum instead of using
dev->usb == NULL as an implicit recovery state.
- Open and validate a recovery
irecv_client_t before serial read/write.
- Check the
DFU_ABORT result before continuing.
- Poll all supported recovery PIDs and match the expected ECID.
- Add a full mocked Path B transition test with
dev->usb == NULL after re-enumeration.
This resolves the reported error only. It must not be described as an A12+ bypass fix.
Option 2: Recommended honest support behavior
Gate the current Path B implementation rather than running destructive operations on unsupported devices:
- Refuse known A12+ chips with a clear message that no functional activation path is integrated.
- Do not label every non-checkm8 or unknown CPID as supported.
- Update the README support matrix to distinguish detection, experimental steps, and verified activation.
- Preserve the recovery lifecycle fix as a separate correctness change if the recovery code is still useful for research.
- Make the final success condition require an actual
Activated state, never merely a successful local command or marker write.
This is consistent with the design discussion in research/fix-issues-hw-usability.md:230-263 and 751-763.
Option 3: Separate A12/A13 research implementation
A real A12/A13 path would require a separate, hardware-specific SecureROM primitive, a compatible boot/jailbreak chain, and a valid activation strategy. This is not a descriptor-index adjustment or a one-line handle fix.
Any such implementation should be separately reviewed, tested on owned hardware, and kept out of the current generic path_b_a12plus module until it can demonstrate real-device activation.
Proposed Acceptance Criteria
- No production path invokes a DFU-only operation after the DFU handle is closed.
- The state machine records DFU, recovery, and normal modes explicitly.
- Recovery re-enumeration is validated by mode and ECID.
- The exact reproduction above no longer emits
No USB handle for DFU mode.
- A full mocked Path B test covers DFU close, recovery open, setenv, reboot, and normal reconnect.
- CPID
0x8020 does not receive a generic “supported” result unless a real activation implementation is present.
- README claims match tested behavior.
- The tool reports failure unless the device's activation state is actually
Activated.
Related Issues
Summary
Path B has two separate problems that are currently presented as one bypass flow:
The first problem is directly reproducible with the existing code and explains #42:
This report is focused on the root cause and possible solutions. It complements #42, #88, #91, #82, and #38 rather than replacing those user reports.
Affected Target
0x802018.7.9path_b_a12plusReproduction
./tr4mpass --force-path-b.The failure does not depend on the USB serial descriptor index. It occurs before the serial descriptor read is attempted.
Expected Behavior
After the DFU handle is closed and recovery mode is detected, Path B should either:
libirecoveryclient for all subsequent recovery operations, orIt must not require a DFU-only
libusb_device_handleafter the device has intentionally left DFU mode.Actual Control Flow
1. DFU handle acquisition
src/main.c:130-151callsusb_dfu_find(), reads CPID/ECID, and stores the opened DFU handle indev->usb.src/device/usb_dfu.c:83-122opens Apple DFUVID 0x05AC / PID 0x1227, detaches the kernel driver, and claims interface 0.2. DFU-to-recovery transition
src/bypass/path_b.c:88-153sendsDFU_ABORT, closes the handle, and explicitly clears the state:The function then creates a temporary libusb context and polls for recovery PID
0x1281.3. Step 2 rejects the intentional state
src/bypass/path_b.c:161-174immediately callspath_b_manipulate_identity()after recovery is detected.src/bypass/path_b_identity.c:331-339still requiresdev->usbto be non-NULL:That guard is incompatible with the previous function, which intentionally closed and nulled the DFU handle.
Why This Is Not Primarily a Descriptor-Index Bug
The current tree already contains the runtime descriptor-index work that an older implementation was missing:
src/device/usb_dfu.c:97-102capturesbDeviceDescriptor.iSerialNumber.src/device/usb_dfu.c:204-215probes the runtime hint followed by legacy indices 3 and 4.src/bypass/path_b_identity.c:118-194uses the cached index with the same fallbacks.src/exploit/checkm8_spray.c:191-201passes the runtime index into checkm8 helpers.A descriptor-index failure would result in a CPID/ECID parsing or serial-read error. The observed error is emitted at
path_b_identity.c:337, beforepath_b_read_serial()is reached.There are still transition issues worth fixing:
src/bypass/path_b.c:104-106ignores the return value of the rawDFU_ABORTtransfer.0x1281, whilepath_b_identity.c:267-276accepts recovery modes corresponding to0x1280..0x1283.Existing Recovery-Side Code
The repository already has code that appears intended to handle the post-transition state:
src/bypass/path_b_identity.c:197-227reads the serial throughlibirecoverywhendev->usb == NULL.src/bypass/path_b_identity.c:231-305opens recovery mode and usesirecv_setenv()/irecv_saveenv().src/bypass/path_b.c:184-249later reopens recovery mode for the reboot-to-normal step.The stale DFU-only precondition makes this recovery branch unreachable from the real Path B orchestration.
Test Coverage Gap
tests/integration/test_path_b_e2e.c:19-21explicitly avoids executingpath_b_module.execute()because the real transition polls can take up to 60 seconds.The identity test at
tests/integration/test_path_b_e2e.c:82-128injects a fake non-NULL USB handle. It therefore cannot catch the production sequence wherestep_reboot_to_recovery()setsdev->usb = NULL.A full mocked state-machine test is needed with these states:
Compatibility Finding: A12+ Support Is Not Functional
The repository's chip table correctly marks CPID
0x8020as A12 and non-checkm8-vulnerable:include/device/chip_db_table_rop.h:175-190tests/test_chip_db.c:37-42src/exploit/checkm8.c:28-43However, Path B accepts any non-checkm8 device:
src/bypass/path_b.c:52-77The wrapper also labels every nonzero CPID as supported:
start-helpers.sh:384-393The README claims functional Path B support for A12 through A17:
README.md:6-8README.md:68-78README.md:193-199The current Path B implementation does not contain an A12 SecureROM exploit. It changes an iBoot environment value and then attempts to use locally fabricated session data. The activation record intentionally contains empty SEP-bound fields:
src/activation/record.c:157-162src/activation/record.c:218-231The offline session code also states that the locally fabricated response requires a patched
mobileactivationd:src/activation/session.c:78-83src/activation/session.c:281-297Path B never performs the Path A jailbreak/mobileactivationd replacement. The integration test documents the expected A12+ activation rejection rather than a successful activation:
tests/integration/test_path_b_e2e.c:132-187Therefore, removing the stale handle guard would fix the immediate error but would not make iPhone XS Max / A12 / iOS 18.7.9 activation functional.
Possible Solutions
Option 1: Minimal state/lifecycle fix
Make the existing recovery implementation reachable and correct:
dev->usbprecondition frompath_b_manipulate_identity().dev->usb == NULLas an implicit recovery state.irecv_client_tbefore serial read/write.DFU_ABORTresult before continuing.dev->usb == NULLafter re-enumeration.This resolves the reported error only. It must not be described as an A12+ bypass fix.
Option 2: Recommended honest support behavior
Gate the current Path B implementation rather than running destructive operations on unsupported devices:
Activatedstate, never merely a successful local command or marker write.This is consistent with the design discussion in
research/fix-issues-hw-usability.md:230-263and751-763.Option 3: Separate A12/A13 research implementation
A real A12/A13 path would require a separate, hardware-specific SecureROM primitive, a compatible boot/jailbreak chain, and a valid activation strategy. This is not a descriptor-index adjustment or a one-line handle fix.
Any such implementation should be separately reviewed, tested on owned hardware, and kept out of the current generic
path_b_a12plusmodule until it can demonstrate real-device activation.Proposed Acceptance Criteria
No USB handle for DFU mode.0x8020does not receive a generic “supported” result unless a real activation implementation is present.Activated.Related Issues