v1.6.0: ATR-hunt deadlock + idle-loop trap fixes

Add hunt-side live ETU recovery so a card already at a non-default F/D
(e.g. Samsung F=512/D=32 -> 16 CLK/bit) can be decoded without a 372-rate
ATR, and bound the all-0xFF hunt so an idle/unsyncable line can never loop
forever.

- _recover_etu_in_hunt(): measure the live ETU from DATA edges while still
  in FIND START, then re-derive clock_skip; reuses _confirm_etu validation.
- _atr_hunt_failure_action(): pure recover/retry/resume_data decision, with
  ATR_HUNT_ETU_ATTEMPTS=3 / ATR_HUNT_IDLE_RESETS=3 / ATR_HUNT_FAIL_LIMIT=12.
- Persistent counters reset only on a successful ATR parse (not RST re-arm),
  so RST-cycling phones cannot starve the escape.
- 8 new unit tests (55 total).

Validated on the full trace suite (test_8 56/0/OK, test_7 65/0/OK, xiaomi
640, xiaomi_cold 473, 4gmodem 327, A55 407/395, S21p 399/375, target S21p
369; 0 CONCAT, RESULT: OK) with no regressions.
This commit is contained in:
Трошин Антон Валерьевич committed 2026-09-11 17:45:54 +03:00
1 parent b7a6e50fc8
commit bca1b85b94
3 files changed
+237 -13

No files matched your search

+49
View File
@@ -255,6 +255,55 @@ mis-frame at the source) is the next decoder step; until then the gate
makes the corruption visible in live and offline captures instead of
silently producing wrong APDUs.
### v1.6.0: ATR-hunt deadlock + idle-loop trap fixes
A live Samsung cold-boot capture that never emits a 372-rate ATR exposed two
defects in the ATR hunt (`state == 'FIND START'`):
1. **Deadlock.** `RST low at start` arms the hunt at the spec-default
`clock_skip=372` with `bit_samples=None`. The ETU-recovery routine
`_measure_etu()` is only reachable from the `DATA` state, which the hunt
can only enter after reading a valid `TS=0x3B/0x3F` (or a plausible
CLA/INS header) **at 372**. A card already running at a non-default F/D
(Samsung `F=512/D=32` -> 16 CLK/bit) produces none, so the hunt reads
idle `0xFF` forever and never reaches the ETU measurement -- a circular
dependency. This is why Samsung (non-default F/D) fails where
default-F/D devices succeed: not clock rate, but `F/D`.
2. **Trap.** The all-`0xFF` branch unconditionally reset `_atr_hunt_count=0`
and returned to `FIND START`, so the bounded fall-through to `DATA` was
unreachable. With RST re-arm also resetting the counter, the hunt could
spin forever.
Fixes:
- **Hunt-side ETU recovery.** `_recover_etu_in_hunt()` runs `_measure_etu()`
+ `_derive_clock_skip_from_etu()` from the hunt after the default-rate
hunt has already failed a few rounds, so a fast device is read at its
true rate. It reuses the DATA-path `_confirm_etu()` validation, so a
noise-locked ETU self-clears after 2 parity failures. No-op for normal
ATRs (parsed on hunt 1).
- **Bounded escape.** `_atr_hunt_failure_action(byte)` (pure, unit-tested)
returns `recover` / `retry` / `resume_data`. ETU recovery every 2 failed
rounds up to 3 times; then a hard bound (`_atr_fail_total >= 12`) or the
all-`0xFF` idle cap (3) falls through to the existing "resuming DATA
without synthetic ATR" path (clearing `bit_samples` so the DATA path
re-measures).
- **Persistent counters.** `_atr_fail_total`, `_hunt_etu_attempts`,
`_atr_idle_resets` are reset only on a successful ATR parse, **not** on
RST re-arm -- otherwise a phone that power-cycles RST while never sending
a valid ATR would starve the escape. Constants: `ATR_HUNT_ETU_ATTEMPTS=3`,
`ATR_HUNT_IDLE_RESETS=3`, `ATR_HUNT_FAIL_LIMIT=12`.
Verification: no regression on the full trace suite (test_8 56/0/OK,
test_7 65/0/OK, xiaomi 640, xiaomi_cold 473, 4gmodem 327, A55 407/395,
S21p 399/375, target S21p_coldboot 369, all `RESULT: OK`, 0 CONCAT);
54/54 unit tests (7 new hunt-escape regression tests). VERSION -> 1.6.0.
Known limitation: no on-disk fixture reproduces the exact live failure (all
real captures contain a 372 ATR), so end-to-end proof needs a re-captured
`.sr` from the failing phone; the unit tests cover the loop-escape logic.
### v1.5.0: invalid-INS one-byte re-frame (residual CONCATs → 0)
The last CONCAT family (4gmodem/A55_sim2/S21p_sim2) is now **reframed at