Conversation
…d a specific error code.
`next_event_ahead` is called from `ux_sync_wait` in two different situations, and rejecting unconditionally broke the first one: * no command being processed (idle home screen): an incoming APDU is the normal way of receiving work. Returning `false` here meant `ux_sync_wait(true)` could never report `UxSyncRetApduReceived`, so an app using the blocking `NbglHomeAndSettings::show()` answered 0x6901 to every APDU and became unreachable from the host. * a command being processed (review, status, ... screen): an incoming APDU is a double APDU and must be answered CmdNotAccepted. io_legacy had no state distinguishing the two: `event_pending` cannot serve, as `check_event` clears it before handing the command to the application. Add `Comm::apdu_in_progress`, set when a command is handed to the application and cleared when the application replies, and gate the rejection on it. This also lets `apdu_send` stop clearing `event_pending`, which silently discarded queued commands on every reply path instead of answering them. Take the decision on the raw frame rather than after `detect_apdu`: `decode_event` overwrites `apdu_buffer`, `apdu_type`, `rx` and `rx_length`, i.e. state owned by the command still being processed. BOLOS APDUs (CLA 0xB0) and frames too short to hold a header keep falling through to `check_event` so their existing handling is preserved. Reject via a dedicated `reject_apdu` that transmits the status word on the intruder's own transport and leaves every response and receive field untouched. Going through `Comm::reply`/`apdu_send` instead would flush any bytes the application had staged before showing the screen (sending them to the host with the error, then losing them from the real response), overwrite `apdu_type` so the in-flight command replied on the wrong channel, and consume a SEPH event, which can drop the user's tap. Also mark `NbglHomeAndSettings::show()` with `#[deprecated]`, which its documentation already stated in prose only, and bump to 1.37.0: the new `StatusWords::CmdNotAccepted` variant breaks downstream exhaustive matches over that public enum. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirror the io_legacy change in the io_new backend, and fix three behaviours of the double-APDU rejection there. `Comm::pending_apdu` was used as the trigger, but it means "an APDU was decoded ahead and not consumed yet", not "a command is being processed". Rejecting on it deferred the answer to the *next* polling iteration, so a screen completing in between discarded the intruder with no response at all: `CommandResponse::send` clears `pending_apdu` unconditionally. Add `Comm::apdu_in_progress`, set when a command is handed to the application and cleared when the application replies, and reject on the iteration that detects the APDU. The `pending_apdu` branch is kept for the case where no command is in flight and the displayed screen does not exit on APDU: answering there is what lets the polling loop, and so the screen, keep running. Stop gating the inline BOLOS (CLA 0xB0) handling behind `stack_usage`. Default builds set `pending_apdu` for those APDUs and then answered CmdNotAccepted, whereas `next_command` dispatches them unconditionally, so OS level requests failed while a screen was displayed. Save and restore `apdu_in_progress` around the inline call, as the BOLOS reply goes through `begin_response().send()` and would otherwise be taken for the reply to the command still being processed. Answer `DecodedEventType::ApduError` instead of ignoring it: a malformed APDU arriving during a screen used to fall into the catch-all arm and get no status word, leaving the host waiting, while `next_command` and io_legacy both reply BadLen. Reject through a new `Comm::reject_apdu`, which transmits the status word on the transport the rejected APDU arrived on and stages nothing into the shared buffer. `decode_apdu` overwrites `apdu_type` before the rejection is decided, so it is captured on entry and restored on every path that does not deliver the APDU to the application; otherwise the in-flight command's response was sent on the intruder's channel. This also removes the `Reply(StatusWords::CmdNotAccepted as u16)` cast and the call back through `reply_status_impl`, which re-resolved the erased global to build a second `&mut Comm` aliasing the one already in hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fix: If incoming APDU occurs during previous APDU processing then send a specific error code.
mbrousset-ledger
approved these changes
Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.