Skip to content

fix(session): always create bulk decompressor; drop compressed FastPath without one#1255

Open
Greg Lamberson (glamberson) wants to merge 2 commits into
Devolutions:masterfrom
lamco-admin:fix/compressed-fastpath-without-decompressor
Open

fix(session): always create bulk decompressor; drop compressed FastPath without one#1255
Greg Lamberson (glamberson) wants to merge 2 commits into
Devolutions:masterfrom
lamco-admin:fix/compressed-fastpath-without-decompressor

Conversation

@glamberson
Copy link
Copy Markdown
Contributor

Closes #1193

Problem

Microsoft RDP servers send compressed FastPath updates in some scenarios (notably full-frame redraws after a window resize or fullscreen toggle) regardless of whether the client advertised compression in the Client Info PDU.

The previous code path:

  1. ActiveStage::new gated decompressor creation on connection_result.compression_type.is_some(). With the default ironrdp-client config (no --compression-level), this is None and no decompressor was created.

  2. fast_path::Processor, when receiving a compressed update with no decompressor, logged a warning and forwarded the still-compressed bytes downstream. The downstream PDU decoder then read what it thought was a length field from compressed payload and failed with NotEnoughBytes { received: N, expected: <huge> }, killing the session.

This is exactly the symptom in #1193: Received compressed FastPath data but no decompressor is configured followed by ColorPointerAttribute::decode NotEnoughBytes { received: 170, expected: 57966 } on a fullscreen toggle against a Windows server.

Fix

  1. Always create a bulk decompressor in ActiveStage::new. Default to Rdp61 (XCRUSH) when no compression type was negotiated; the per-update compression type from the FastPath PDU header is passed through to decompress per call, so the decompressor handles whatever variant the server actually sends.

  2. As defense in depth, change the no-decompressor branch in process_fast_path_update from "pass compressed bytes through" to "drop the update with a warning". A dropped update loses one frame's worth of pixels; forwarding compressed bytes corrupts the session.

Notes

  • The bulk_decompressor: Option<BulkCompressor> field is preserved (no API change). Library consumers that build a fast_path::Processor directly can still pass None; if they do, compressed updates are dropped rather than crashing the session.
  • The compression-flags-without-COMPRESSED-bit branch (update_pdu.data passthrough with the FIXME-shaped comment about informing the decompressor of FLUSHED/AT_FRONT flags) is unchanged. That's a separate not-yet-implemented requirement.

Test

  • cargo check -p ironrdp-session clean
  • cargo clippy -p ironrdp-session --all-targets clean (only pre-existing MSRV / ironrdp-error warnings unrelated to this change)
  • cargo test -p ironrdp-session --lib passes (no new tests added; the path is exercised by integration against real servers)

Greg Lamberson added 2 commits May 10, 2026 12:43
…th without one

Closes Devolutions#1193

Microsoft RDP servers send compressed FastPath updates in some
scenarios (notably full-frame redraws after a window resize or
fullscreen toggle) regardless of whether the client advertised
compression in the Client Info PDU. The previous code path:

1. ActiveStage::new gated decompressor creation on
   `connection_result.compression_type.is_some()`. With the default
   `ironrdp-client` config (no `--compression-level`), this is `None`
   and no decompressor was created.

2. fast_path::Processor, when receiving a compressed update with no
   decompressor, logged a warning and forwarded the still-compressed
   bytes downstream. The downstream PDU decoder then read what it
   thought was a length field from compressed payload and failed with
   `NotEnoughBytes { received: N, expected: <huge> }`, killing the
   session.

Fix:

1. Always create a bulk decompressor in ActiveStage::new. Default to
   Rdp61 (XCRUSH) when no compression type was negotiated; the
   per-update compression type from the FastPath PDU header is passed
   through to `decompress` per call, so the decompressor handles
   whatever variant the server actually sends.

2. As defense in depth, change the no-decompressor branch in
   process_fast_path_update from "pass compressed bytes through" to
   "drop the update with a warning". A dropped update loses one
   frame's worth of pixels; forwarding compressed bytes corrupts the
   session.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Crash on changing window size

1 participant