Fix touch responsiveness and USB HID issues#777
Conversation
acf9e87 to
a05c40b
Compare
3065d91 to
f64ddf4
Compare
ia0
left a comment
There was a problem hiding this comment.
Thanks for the PR! I'll only comment on the Touch issue and let @kaczmarczyck address the second point.
| if let Some(state) = this.take() { | ||
| state.button.stop(); | ||
| state.touched.store(true, Relaxed); | ||
| } |
There was a problem hiding this comment.
We should not reach unreachable!(). I wonder if you had to add this because you unconditionally reset the STATE in Drop for Touch. In theory, state.button.stop() will start dropping future events and clear any existing pending events. If that's not the case it's a bug in Wasefire.
That said, the applet could be resilient against Wasefire bugs. I don't think it's a good idea because (at least for development) we want to fail as soon as something impossible happens. We could have a resilient cargo feature to avoid crashes as much as possible, which would be used for production keys (there could be security concerns though, it's a trade-off with availability). @kaczmarczyck what do you think?
There was a problem hiding this comment.
Thank you very much for explaining!
I removed this diff. I don't want to make development more difficult 😄
There was a problem hiding this comment.
We should double check how we deal with parallel connections, and that we don't miss anything else. We didn't test enough since the Wasefire refactoring.
Touches should never be used twice. CTAP2 library code should already ensure this, but the combination with CTAP1 can work in one direction: First requesting on CTAP1, then going into the CTAP2 UP loop. We should drop CTAP1 then. But doesn't need to be done here.
There was a problem hiding this comment.
And to answer the question, once it is impossible, we can fail when the impossible happens.
This commit addresses responsiveness issues and CTAP protocol deviations in the USB HID transport and user presence handling: 1. User Presence State Leak: When a user presence request is interrupted (e.g., by a timeout or an incoming USB packet), the touch state was incorrectly left running in the background. This prevented subsequent LED blinking and caused a panic if hardware interrupts (bounces) were queued. The `Touch` struct now correctly implements `Drop` to clear the state, and event handling safely ignores stale interrupts. 2. Transaction Timeout Race Condition: Following a transaction timeout (CTAP 2.1 Section 11.2.5.2), the firmware incorrectly dropped the next incoming packet while returning a `MsgTimeout` error. If the host initiated a new transaction with a `CTAPHID_INIT` packet immediately after a timeout, the new initialization packet was discarded, leaving the host waiting indefinitely. The timeout logic now preserves new initialization packets and only returns a timeout error if the host sends a continuation packet for the aborted message.
f64ddf4 to
ff90a86
Compare
ia0
left a comment
There was a problem hiding this comment.
LGTM for src/touch.rs. Is there a reason the PR is still a draft?
kaczmarczyck
left a comment
There was a problem hiding this comment.
Approving for HID library code
| // Otherwise, proceed with processing the packet. | ||
| if cid == current_cid { | ||
| if cid == current_cid | ||
| && matches!(processed_packet, ProcessedPacket::ContinuationPacket { .. }) |
There was a problem hiding this comment.
Thanks for reporting this bug in a very old part of the code. I wrote a test to check the new behavior, feel free to add it here, or I will later:
#[test]
fn test_timed_out_new_init() {
let mut env = TestEnv::default();
let mut assembler = MessageAssembler::default();
assert_eq!(
assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
None,
),
Ok(None)
);
env.clock().advance(TIMEOUT_DURATION_MS);
assert_eq!(
assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x81, 0x00, 0x40]),
None
),
Ok(None)
);
assert_eq!(
assembler.parse_packet(
&mut env,
&zero_extend(&[0x12, 0x34, 0x56, 0x78, 0x00]),
None
),
Ok(Some(Message {
cid: [0x12, 0x34, 0x56, 0x78],
cmd: CtapHidCommand::Ping,
payload: vec![0x00; 0x40]
}))
);
}
This PR addresses two responsiveness issues that may cause the device to hang or drop connections:
DropforTouchto correctly clear theSTATEwhen a user presence request is interrupted (e.g., by a USB packet or timeout). Added safe extraction inState::touchto handle queued hardware button interrupts (bounces) without hittingunreachable!()and panicking.MessageAssembler::parse_packetto allow any valid initialization packet (not justCTAPHID_INIT) to abort a hung transaction, in accordance with the CTAP 2.1 specification.MsgTimeouterror.These fixes prevent the firmware from entering unrecoverable states where the LED fails to blink or the device stops responding to the host.