refactor(pdu): replace unreachable!() in capability_sets/ with exhaustive matching#1328
Conversation
…tive matching Two `_ => unreachable!()` sites in `crates/ironrdp-pdu/src/rdp/capability_sets/` become compile-time exhaustive matches as part of the audit pass on issue Devolutions#1314. Both sites are class (a) (provably unreachable in current code); the hardening turns a future regression from a runtime panic into a compile error. `mod.rs:448` (inner match in `CapabilitySet::Encode::encode`'s raw-buffer fallback): the `_ => unreachable!()` catch-all is replaced with an explicit enumeration of the 17 structured `CapabilitySet` variants that are already handled by the outer match's specific arms. The new structured-variants arm keeps `unreachable!()` as its body because it is unreachable under the current outer-match structure. The change is that adding a new variant to `CapabilitySet` is now a compile error here until the new variant is routed in this `Encode` impl. PR Devolutions#1313 (BitmapCacheV3 encoder `unreachable!()` reached on decoder-accepted input) demonstrated why a runtime catch-all is the wrong shape for this match. `bitmap_codecs/mod.rs:284` (nested match in `CodecProperty` decode for `GUID_REMOTEFX | GUID_IMAGE_REMOTEFX`): the inner `match guid { ... _ => unreachable!() }` carried a dead arm because the outer match already guaranteed `guid` was one of the two values. Replaced with an `if guid == GUID_REMOTEFX { ... } else { ... }`, eliminating the unreachable branch entirely. Neither site was a class (b) (reachable via fuzz) or class (c) (reachable via attacker-controlled wire inputs) per the static analysis and empirical fuzz evidence (`pdu_round_trip` from PR Devolutions#1291 and `message_decoding_invariants` from PR Devolutions#1320 have exercised `CapabilitySet` and `ShareControlHeader` for millions of iterations post-Devolutions#1313 with no panics). Refs Devolutions#1314.
There was a problem hiding this comment.
Pull request overview
This PR is part of the ironrdp-pdu audit work (issue #1314) to remove runtime unreachable!() catch-alls in capability-set code paths and replace them with structures that either become compile-time exhaustive (for enum variants) or remove provably-dead branches (for GUID-discriminated decoding). The goal is to turn future regressions from runtime panics into compile errors or eliminate unreachable branches entirely.
Changes:
- In
CapabilitySet::encode, replaced an inner_ => unreachable!()with an explicit list of the structuredCapabilitySetvariants, so adding a new enum variant forces this site to be updated at compile time. - In
Codecdecoding forGUID_REMOTEFX | GUID_IMAGE_REMOTEFX, removed a dead nestedmatch ... _ => unreachable!()and replaced it with anif/elsebased on the already-constrainedguid.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| crates/ironrdp-pdu/src/rdp/capability_sets/mod.rs | Makes the raw-buffer fallback’s inner match exhaustive w.r.t. CapabilitySet variants, preventing future missing-variant bugs from compiling. |
| crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs | Removes an unreachable nested match arm in RemoteFX property decoding by leveraging the outer match’s constraint on guid. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| match guid { | ||
| GUID_REMOTEFX => CodecProperty::RemoteFx(property), | ||
| GUID_IMAGE_REMOTEFX => CodecProperty::ImageRemoteFx(property), | ||
| _ => unreachable!(), | ||
| // `guid` is `GUID_REMOTEFX` or `GUID_IMAGE_REMOTEFX` by the | ||
| // outer match arm; the previous nested `match` plus | ||
| // `_ => unreachable!()` carried a dead arm. | ||
| if guid == GUID_REMOTEFX { | ||
| CodecProperty::RemoteFx(property) | ||
| } else { | ||
| CodecProperty::ImageRemoteFx(property) | ||
| } |
There was a problem hiding this comment.
thought: I would keep the unreachable!() here rather than fold it into an if/else. guid is already validated as GUID_REMOTEFX | GUID_IMAGE_REMOTEFX by the outer arm, so the _ branch is genuinely dead -- and it's because it should be dead that unreachable!() is the right tool: it's a redundant correctness check that fires loudly under tests/fuzzing if a future change (e.g. a third GUID added to the outer arm) breaks the invariant. The else turns that into a silent mislabel instead. Note this branch isn't reachable from the wire, only from a logic regression, so the usual "no panics in decoders" rule doesn't apply.
Optionally add a message to document the invariant inline: unreachable!("guid validated as RemoteFX or ImageRemoteFX by the outer match").
The other exhaustive-match changes in the PR look good to me.
Two
_ => unreachable!()sites incrates/ironrdp-pdu/src/rdp/capability_sets/become compile-time exhaustive matches as part of the audit pass on issue #1314.
Both sites are class (a) (provably unreachable in current code); the
hardening turns a future regression from a runtime panic into a compile error.
mod.rs:448(inner match inCapabilitySet::Encode::encode's raw-bufferfallback): the
_ => unreachable!()catch-all is replaced with an explicitenumeration of the 17 structured
CapabilitySetvariants that are alreadyhandled by the outer match's specific arms. The new structured-variants arm
keeps
unreachable!()as its body because it is unreachable under thecurrent outer-match structure. The change is that adding a new variant to
CapabilitySetis now a compile error here until the new variant is routedin this
Encodeimpl. PR #1313 (BitmapCacheV3 encoderunreachable!()reached on decoder-accepted input) demonstrated why a runtime catch-all is
the wrong shape for this match.
bitmap_codecs/mod.rs:284(nested match inCodecPropertydecode forGUID_REMOTEFX | GUID_IMAGE_REMOTEFX): the innermatch guid { ... _ => unreachable!() }carried a dead arm because the outer match alreadyguaranteed
guidwas one of the two values. Replaced with anif guid == GUID_REMOTEFX { ... } else { ... }, eliminating the unreachable branchentirely.
Neither site was a class (b) (reachable via fuzz) or class (c) (reachable
via attacker-controlled wire inputs) per the static analysis and empirical
fuzz evidence (
pdu_round_tripfrom PR #1291 andmessage_decoding_invariantsfrom PR #1320 have exercised
CapabilitySetandShareControlHeaderformillions of iterations post-#1313 with no panics).
Refs #1314.