Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions crates/ironrdp-pdu/src/input/fast_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,13 @@ impl<'de> Decode<'de> for FastPathInputEvent {
FastpathInputEventType::ScanCode => {
ensure_size!(in: src, size: 1);
let code = src.read_u8();
let flags = KeyboardFlags::from_bits(flags)
.ok_or_else(|| invalid_field_err!("flags", "input keyboard flags unsupported", in: src))?;
// Retain unknown eventFlags bits (crate-wide policy since
// #1144): the slow-path decoder for the same keystroke
// already does (scan_code.rs), and a rejection here is
// session-fatal mid-use. [MS-RDPBCGR] 3.3.5.8.2's SHOULD-drop
// covers unknown event TYPES, which stays enforced above,
// not unknown flag bits.
let flags = KeyboardFlags::from_bits_retain(flags);
FastPathInputEvent::KeyboardEvent(flags, code)
}
FastpathInputEventType::Mouse => {
Expand All @@ -215,15 +220,16 @@ impl<'de> Decode<'de> for FastPathInputEvent {
FastPathInputEvent::MouseEventRel(mouse_event)
}
FastpathInputEventType::Sync => {
let flags = SynchronizeFlags::from_bits(flags)
.ok_or_else(|| invalid_field_err!("flags", "input synchronize flags unsupported", in: src))?;
// Same rationale as ScanCode above; the slow-path sync
// decoder (sync.rs) already retains unknown toggle bits.
let flags = SynchronizeFlags::from_bits_retain(flags);
FastPathInputEvent::SyncEvent(flags)
}
FastpathInputEventType::Unicode => {
ensure_size!(in: src, size: 2);
let code = src.read_u16();
let flags = KeyboardFlags::from_bits(flags)
.ok_or_else(|| invalid_field_err!("flags", "input keyboard flags unsupported", in: src))?;
// Same rationale as ScanCode above.
let flags = KeyboardFlags::from_bits_retain(flags);
FastPathInputEvent::UnicodeKeyboardEvent(flags, code)
}
FastpathInputEventType::QoeTimestamp => {
Expand Down
49 changes: 49 additions & 0 deletions crates/ironrdp-testsuite-core/tests/input/fastpath_packets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,52 @@ fn wheel_rotations_out_of_range_is_clamped(#[case] rotation_units: i16, #[case]
})
);
}

#[test]
fn fastpath_keyboard_event_with_undefined_flag_bits_decodes_and_retains_them() {
// The 5-bit fast-path eventFlags field defines RELEASE, EXTENDED and
// EXTENDED1 for keyboard events ([MS-RDPBCGR] 2.2.8.1.2.2.1); an unknown
// bit must not kill a live session, and the slow-path decoder for the
// same keystroke already tolerates it. The bit is retained so
// re-encoding preserves the wire value.
const UNDEFINED_BIT: u8 = 0x10;

// eventCode = FASTPATH_INPUT_EVENT_SCANCODE (0) in bits 5..8,
// eventFlags = RELEASE | UNDEFINED_BIT in bits 0..5, then the scancode.
let buffer = [0x01 | UNDEFINED_BIT, 0x1d];

let event: FastPathInputEvent = ironrdp_core::decode(&buffer).expect("undefined flag bits are not fatal");

let FastPathInputEvent::KeyboardEvent(flags, code) = event else {
panic!("expected a keyboard event");
};
assert_eq!(code, 0x1d);
assert!(flags.contains(KeyboardFlags::RELEASE));
assert_ne!(flags.bits() & UNDEFINED_BIT, 0);
assert_eq!(
ironrdp_core::encode_vec(&FastPathInputEvent::KeyboardEvent(flags, code)).unwrap(),
buffer
);
}

#[test]
fn fastpath_sync_event_with_undefined_flag_bits_decodes_and_retains_them() {
// [MS-RDPBCGR] 2.2.8.1.2.2.5 defines the four lock flags; bit 0x10 of
// the 5-bit eventFlags field is undefined and must not be fatal.
const UNDEFINED_BIT: u8 = 0x10;

// eventCode = FASTPATH_INPUT_EVENT_SYNC (3) in bits 5..8.
let buffer = [(3 << 5) | 0x01 | UNDEFINED_BIT];

let event: FastPathInputEvent = ironrdp_core::decode(&buffer).expect("undefined flag bits are not fatal");

let FastPathInputEvent::SyncEvent(flags) = event else {
panic!("expected a sync event");
};
assert!(flags.contains(SynchronizeFlags::SCROLL_LOCK));
assert_ne!(flags.bits() & UNDEFINED_BIT, 0);
assert_eq!(
ironrdp_core::encode_vec(&FastPathInputEvent::SyncEvent(flags)).unwrap(),
buffer
);
}
Loading