Skip to content

fix(cable): propagate post-handshake errors from protocol::connection#217

Merged
AlfioEmanueleFresta merged 4 commits into
masterfrom
feat/pxp-ble-error-propagation
May 18, 2026
Merged

fix(cable): propagate post-handshake errors from protocol::connection#217
AlfioEmanueleFresta merged 4 commits into
masterfrom
feat/pxp-ble-error-propagation

Conversation

@AlfioEmanueleFresta
Copy link
Copy Markdown
Member

@AlfioEmanueleFresta AlfioEmanueleFresta commented May 17, 2026

Summary

Stacked on feat/pxp-ble (#216).

protocol::connection previously returned () and swallowed every post-handshake fault via let _ = ...: Noise decrypt/encrypt failures, malformed framing inside the encrypted channel, dropped CBOR-response receivers and transport-level I/O errors. The connection silently degraded (subsequent frames keep failing the same way) and the channel eventually moved to Terminated without ever emitting CableUxUpdate::CableUpdate(CableUpdate::Error(_)). Subscribers had no way to distinguish a clean close from a fault.

Behaviour changes

  • protocol::connection returns Result<(), TransportError>. Each arm of the select loop is explicit about whether a condition is fatal (exit) or benign (continue + log).
  • A new TransportError::EncryptionFailed variant is emitted for Noise transport-mode encrypt/decrypt failures, distinct from handshake-time NegotiationFailed and generic ConnectionFailed.
  • A peer-sent CableTunnelMessageType::Shutdown is treated as a clean close (returns Ok(())) instead of an "unexpected" error.
  • Malformed or unsigned linking updates are logged and dropped without tearing the channel down: the Noise state is intact and only that one update is unusable. Matches Chromium.
  • qr_code_device and known_devices route Err(_) from the tunnel loop through UxUpdateSender::send_error, which already transitions to Terminated.

Test plan

  • cargo check --workspace --all-targets
  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo fmt --all -- --check

@AlfioEmanueleFresta AlfioEmanueleFresta marked this pull request as ready for review May 17, 2026 16:59
Copy link
Copy Markdown
Collaborator

@msirringhaus msirringhaus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, some things could be simplified a bit

Err(e) => {
error!(?e, "Failed to decode CABLE tunnel message");
return Err(Error::Transport(TransportError::ConnectionFailed));
return Err(TransportError::InvalidFraming);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit convoluted. CableTunnelMessage::from_slice() only returns Error::Transport(TransportError::InvalidFraming) on error, which we throw away here and hardcode it to TransportError::InvalidFraming again. from_slice() could just return a TransportError which we log here and send along.

let known_device = CableKnownDeviceInfo::new(tunnel_domain, linking_info)?;
) -> Result<CableKnownDeviceInfo, TransportError> {
let known_device = CableKnownDeviceInfo::new(tunnel_domain, linking_info)
.map_err(|_| TransportError::InvalidFraming)?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. CableKnownDeviceInfo::new() returns InvalidFraming anyways, and we throw away the error and hardcode it again.

Base automatically changed from feat/pxp-ble to master May 18, 2026 18:55
Distinct from NegotiationFailed (handshake-time) and from generic
ConnectionFailed so post-handshake Noise crypto failures can be
surfaced explicitly. Used by the next commit in cable/protocol.rs.
protocol::connection previously returned () and swallowed every
post-handshake fault via 'let _ = ...': Noise decrypt/encrypt
failures, malformed framing inside the encrypted channel, dropped
CBOR-response receivers and transport-level I/O errors. The
connection silently degraded (subsequent frames keep failing the
same way) and the channel eventually moved to Terminated without
the caller ever learning what went wrong.

Return Result<(), TransportError> instead, and replace each 'let _'
in the select loop with explicit handling that exits on real faults
and continues on benign ones:

- Noise transport-mode encrypt/decrypt fail -> EncryptionFailed.
- Outer cable-frame parse / oversize CBOR -> InvalidFraming.
- Dropped CBOR-response receiver / I/O error -> propagate.
- Malformed update message or bad linking signature -> log and
  continue (the Noise channel is intact; only that one update is
  unusable, matching Chromium's behaviour).
- Peer-sent CableTunnelMessageType::Shutdown -> return Ok(())
  via a new RecvOutcome::PeerShutdown.

Helpers below the loop now return TransportError directly so the
errors flow through without Error<->TransportError gymnastics at
the top.
Both the QR-code and known-device channel tasks called
protocol::connection(tunnel_input).await; ux_sender.set_connection_state(Terminated).await
and discarded the result. With the previous commit propagating
post-handshake faults, route Err(e) through send_error so the
broadcast subscribers see a CableUpdate::Error before the channel
state transitions to Terminated. send_error already performs the
Terminated transition itself.
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the feat/pxp-ble-error-propagation branch from 7888d0f to 8cb03ac Compare May 18, 2026 19:04
@AlfioEmanueleFresta AlfioEmanueleFresta merged commit 700e262 into master May 18, 2026
@AlfioEmanueleFresta AlfioEmanueleFresta deleted the feat/pxp-ble-error-propagation branch May 18, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants