diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 9de20588e..d4c31453e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1508,7 +1508,11 @@ impl Remote { _ => {} }, Some(message::Union::CursorData(cd)) => { - self.handler.set_cursor_data(cd); + let id = cd.id; + match decode_cursor_data(cd) { + Ok(cd) => self.handler.set_cursor_data(cd), + Err(err) => log::warn!("Rejected cursor {id}: {err}"), + } } Some(message::Union::CursorId(id)) => { self.handler.set_cursor_id(id.to_string()); @@ -2542,6 +2546,37 @@ impl Remote { } } +// Both UI handlers receive validated, uncompressed RGBA from the receive loop. +fn decode_cursor_data(data: CursorData) -> hbb_common::ResultType { + use hbb_common::{anyhow::anyhow, bail}; + + const MAX_CURSOR_SIZE: i32 = 4096; + const RGBA_CHANNELS: usize = 4; + + let mut cd = data; + // Sciter keeps the legacy outer image; Flutter uses the physical variant. + #[cfg(feature = "flutter")] + if let Some(physical) = cd.high_resolution.take() { + cd = physical; + } + if !(1..=MAX_CURSOR_SIZE).contains(&cd.width) || !(1..=MAX_CURSOR_SIZE).contains(&cd.height) { + bail!("invalid source size {}x{}", cd.width, cd.height); + } + let expected = (cd.width as usize) + .checked_mul(cd.height as usize) + .and_then(|pixels| pixels.checked_mul(RGBA_CHANNELS)) + .ok_or_else(|| anyhow!("cursor RGBA size overflow"))?; + let colors = zstd::bulk::decompress(&cd.colors, expected)?; + if colors.len() != expected { + bail!( + "invalid RGBA length: expected {expected}, got {}", + colors.len() + ); + } + cd.colors = colors.into(); + Ok(cd) +} + struct RemoveJob { files: Vec, path: String, diff --git a/src/flutter.rs b/src/flutter.rs index ace0119b3..c453e866d 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -642,8 +642,7 @@ impl FlutterHandler { impl InvokeUiSession for FlutterHandler { fn set_cursor_data(&self, cd: CursorData) { - let cd = cd.high_resolution.as_ref().unwrap_or(&cd); - let colors = hbb_common::compress::decompress(&cd.colors); + let colors = &cd.colors; self.push_event( "cursor_data", &[ diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 2f36f5d81..1bdf90a55 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -122,7 +122,7 @@ impl SciterHandler { impl InvokeUiSession for SciterHandler { fn set_cursor_data(&self, cd: CursorData) { - let mut colors = hbb_common::compress::decompress(&cd.colors); + let mut colors: Vec = cd.colors.into(); if colors.iter().filter(|x| **x != 0).next().is_none() { log::info!("Fix transparent"); // somehow all 0 images shows black rect, here is a workaround