feat(pdu,graphics,egfx): add ClearCodec bitmap compression codec#1174
feat(pdu,graphics,egfx): add ClearCodec bitmap compression codec#1174Greg Lamberson (glamberson) wants to merge 1 commit into
Conversation
Wire ClearCodec into the EGFX client's WireToSurface1 codec dispatch, following the same pattern as AVC420 and Uncompressed decode. - Add ClearCodecDecoder field (always enabled, no external codec needed) - Decode ClearCodec bitmap data and convert BGRA output to RGBA - Reset decoder caches on ResetGraphics (V-bar + glyph state) - Add 3 integration tests: basic decode, RGBA output, reset survival - Add unit test for BGRA-to-RGBA channel reordering Depends on the ClearCodec codec PR (Devolutions#1174).
There was a problem hiding this comment.
Pull request overview
Adds ClearCodec (MS-RDPEGFX 2.2.4.1) support across the wire-format layer (ironrdp-pdu), codec logic (ironrdp-graphics), and EGFX server API (ironrdp-egfx), with extensive new tests in ironrdp-testsuite-core.
Changes:
- Introduces
ironrdp-pdu::codecs::clearcodecwith residual/bands/subcodec parsing and helpers. - Adds
ironrdp-graphics::clearcodecencoder/decoder plus persistent V-bar and glyph caching. - Extends
GraphicsPipelineServerwithsend_clearcodec_frame()and adds ClearCodec integration tests.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/ironrdp-testsuite-core/tests/graphics/mod.rs | Registers the new ClearCodec integration test module. |
| crates/ironrdp-testsuite-core/tests/graphics/clearcodec.rs | Adds ClearCodec end-to-end and adversarial integration tests. |
| crates/ironrdp-pdu/src/codecs/mod.rs | Exposes the new ClearCodec codec module. |
| crates/ironrdp-pdu/src/codecs/clearcodec/mod.rs | Defines ClearCodec bitmap stream + composite payload parsing and exports layer decoders. |
| crates/ironrdp-pdu/src/codecs/clearcodec/residual.rs | Implements residual (BGR RLE) encode/decode. |
| crates/ironrdp-pdu/src/codecs/clearcodec/bands.rs | Implements bands layer decoding (V-bar references + inline data). |
| crates/ironrdp-pdu/src/codecs/clearcodec/subcodec.rs | Implements subcodec layer parsing (raw/NSCodec/RLEX region headers). |
| crates/ironrdp-pdu/src/codecs/clearcodec/rlex.rs | Implements RLEX subcodec parsing. |
| crates/ironrdp-graphics/src/lib.rs | Exposes the new clearcodec module. |
| crates/ironrdp-graphics/src/clearcodec/mod.rs | Implements ClearCodec decoder/encoder and compositing across layers. |
| crates/ironrdp-graphics/src/clearcodec/glyph_cache.rs | Adds glyph cache backing storage used by encoder/decoder. |
| crates/ironrdp-graphics/src/clearcodec/vbar_cache.rs | Adds V-bar/short V-bar cache backing storage used by decoder. |
| crates/ironrdp-egfx/src/server.rs | Adds send_clearcodec_frame() EGFX server API entry point. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
…items Address review feedback on Devolutions#1174: - Validate subcodec region bounds against surface dimensions - Validate RLEX palette indices and enforce region pixel budget - Remove silent pixel skipping that caused coordinate desync - Add glyph cache dimension mismatch check on hit - Use checked arithmetic for raw subcodec data length - Remove unused expected_seq field from ClearCodecDecoder - Use saturating_mul in encoder for pixel count - Add missing QoE backpressure recording for ClearCodec frames - Document vbar_cache wrap constant derivation
…items Address review feedback on Devolutions#1174: - Validate subcodec region bounds against surface dimensions - Validate RLEX palette indices and enforce region pixel budget - Remove silent pixel skipping that caused coordinate desync - Add glyph cache dimension mismatch check on hit - Use checked arithmetic for raw subcodec data length - Remove unused expected_seq field from ClearCodecDecoder - Use saturating_mul in encoder for pixel count - Add missing QoE backpressure recording for ClearCodec frames - Document vbar_cache wrap constant derivation
012af8b to
8605ed9
Compare
Wire ClearCodec into the EGFX client's WireToSurface1 codec dispatch, following the same pattern as AVC420 and Uncompressed decode. - Add ClearCodecDecoder field (always enabled, no external codec needed) - Decode ClearCodec bitmap data and convert BGRA output to RGBA - Reset decoder caches on ResetGraphics (V-bar + glyph state) - Add 3 integration tests: basic decode, RGBA output, reset survival - Add unit test for BGRA-to-RGBA channel reordering Depends on the ClearCodec codec PR (Devolutions#1174).
8605ed9 to
7113058
Compare
…items Address review feedback on Devolutions#1174: - Validate subcodec region bounds against surface dimensions - Validate RLEX palette indices and enforce region pixel budget - Remove silent pixel skipping that caused coordinate desync - Add glyph cache dimension mismatch check on hit - Use checked arithmetic for raw subcodec data length - Remove unused expected_seq field from ClearCodecDecoder - Use saturating_mul in encoder for pixel count - Add missing QoE backpressure recording for ClearCodec frames - Document vbar_cache wrap constant derivation
Wire ClearCodec into the EGFX client's WireToSurface1 codec dispatch, following the same pattern as AVC420 and Uncompressed decode. - Add ClearCodecDecoder field (always enabled, no external codec needed) - Decode ClearCodec bitmap data and convert BGRA output to RGBA - Reset decoder caches on ResetGraphics (V-bar + glyph state) - Add 3 integration tests: basic decode, RGBA output, reset survival - Add unit test for BGRA-to-RGBA channel reordering Depends on the ClearCodec codec PR (Devolutions#1174).
973f325 to
662548b
Compare
|
Reviewing against Haven's downstream ClearCodec decoder — your factorisation (separate Note that the Haven would drop our |
|
Thanks for the careful review against Haven's downstream implementation, that is exactly the kind of independent confirmation that helps these PRs land. The factoring into separate Cross-linking your RDPGFX_RECT16 finding back to #1175 where the actual fix lives, and confirming this PR's decoder is unaffected since it takes raw |
662548b to
43d1f14
Compare
43d1f14 to
6e142c3
Compare
|
Force-pushed Bugs fixed1. Integer overflow panic in 2. Adversarial-dimensions OOM in 3. Alpha-channel contract was undocumented on Spec audit (MS-RDPEGFX 2.2.4.1)While preparing the fixes I also re-read MS-RDPEGFX 2.2.4.1 against the implementation, using FreeRDP 3.15.0's IronRDP applies these length checks incrementally per-read; another implementation pattern is to apply them upfront in a single shot per layer entry-point. Both styles satisfy the spec-required bounded-read outcome; the spec does not require either over the other. How these were surfacedFor transparency: the three bugs were surfaced by local fuzz oracles (decode + encode/decode round-trip on the codec's public API) before maintainer review. The fuzz infrastructure is on a separate branch and will follow as a small standalone PR after this one merges; the codec's public API is already fuzz-friendly so no prep work is needed in this PR. Verification post-amend: |
6e142c3 to
38f2f31
Compare
|
Greg Lamberson (@glamberson) Thank you very much. Just confirming with you: can I go ahead and merge this PR, or any prerequisite I’m missing? |
Add ClearCodec (MS-RDPEGFX 2.2.4.1) codec support across two crates: ironrdp-pdu: Wire-format decode/encode for all ClearCodec layers (residual BGR RLE, bands with V-bar caching, subcodec dispatch including RLEX palette-indexed RLE). Full round-trip test coverage. ironrdp-graphics: ClearCodecDecoder with persistent V-bar and glyph caches. ClearCodecEncoder with residual-only encoding and glyph deduplication for server-side bitmap compression. Hardening pass before maintainer review (local fuzz oracles surfaced three bug classes that escaped the initial review): - Per-dimension cap at 8192 on decode, replacing the previous per- pixel-count cap at 8192*8192. The pixel-count form accepted degenerate aspect ratios (e.g. 63961x771 = 49M pixels, under the 67M cap) that allocated ~197MB from a few attacker-controlled bytes. The per-dimension form rejects implausible tile shapes regardless of total area while preserving headroom for 8K displays (7680x4320). MS-RDPEGFX caps surfaces at 32767x32767 but does not mandate a separate tile cap; 8192 is a defensive choice that fits realistic tile sizes. - Fix integer overflow at bands::decode_band on `usize::from(x_end - x_start + 1)`. When x_end = u16::MAX and x_start = 0, the `+ 1` overflowed the u16 arithmetic before the usize cast and triggered a release-mode panic under overflow-checks. The existing `if x_end < x_start` guard prevents underflow but not the +1 overflow. Fix is to cast to usize first: `usize::from(x_end - x_start) + 1`. - Document the alpha contract on ClearCodecEncoder::encode and ClearCodecDecoder::decode. ClearCodec is lossless on the three color channels (B, G, R) per spec; the wire format does not transmit alpha. The encoder reads B/G/R from each input pixel and discards alpha; the decoder fills alpha with 0xFF unconditionally. Callers needing alpha must transport it separately. Previously the doc-comment claim of "pixel-perfect fidelity" was ambiguous; alpha-channel mismatches in any round-trip with non-0xFF alpha inputs are by design, not bugs. Local fuzz infrastructure (clearcodec_decode + clearcodec_round_trip oracles) follows in a separate PR once this lands; the codec's public API is already fuzz-friendly so no prep is needed here. A separate spec-driven audit of input-validation completeness (per-layer stream-length pre-checks in subcodec / residual / bands) is queued as follow-up work; deferring from this PR pending review of MS-RDPEGFX 2.2.4.1. Closes Devolutions#1158 ClearCodec subtask.
38f2f31 to
59b3de3
Compare
|
Benoît Cortier (@CBenoit) Yes, no prerequisite blocks merge. Rebasing onto current master ( |
Part of the multi-codec EGFX implementation described in #1158 (Section 3).
Summary
Add ClearCodec (MS-RDPEGFX 2.2.4.1), the mandatory lossless bitmap codec
for all EGFX versions (V8-V10.7). ClearCodec uses three-layer compositing
(residual BGR RLE, bands with V-bar caching, subcodec regions) to encode
text, UI elements, and icons with pixel-perfect fidelity.
This is the first ClearCodec encoder in any open-source RDP implementation.
FreeRDP has client-side decode only.
What it adds
ironrdp-pdu (wire format):
RLE decode/encode, bands with V-bar cache references (full hit, short
hit, short miss), RLEX subcodec, raw/NSCodec dispatch
ironrdp-graphics (codec logic):
ClearCodecEncoder with residual-only encoding and glyph deduplication
ironrdp-egfx (server integration):
ironrdp-testsuite-core:
adversarial input handling (max run_length, OOM dimensions, malformed
streams, uncached glyph hits), bands layer compositing through the full
decode pipeline, cache state management across multi-frame sessions,
compression quality assertions
Security hardening
Three rounds of code review against FreeRDP ClearCodec CVEs:
(MS-RDPEGFX 2.2.4.1.1.2.1.1.3)
NSCodec
The NSCodec subcodec (Layer 3 option) is not implemented in this PR.
The encoder avoids generating NSCodec tiles, and the decoder stub leaves
NSCodec regions at their residual-layer values. This can be added later
without API changes.
Test plan