From daff54387b0d4dc7543ea720edb2aef1c6050235 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 24 Sep 2026 15:05:54 +0800 Subject: [PATCH] fix(clipboard): unix file server, cap a single range read at 16 MiB A peer could still make the server allocate and read up to the 1 GiB frame limit per request. Cap the data a single range read returns at 16 MiB. The read is clamped to the rest of the file first, and a larger one is refused rather than shortened: a short response reads as end of file to IStream callers on Windows and would silently truncate the copy. macOS requests 4 MiB per range (BLOCK_SIZE) and FUSE reads are smaller. A Windows client forwards the application's IStream::Read size, so only an application reading more than 16 MiB in one call is affected. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../clipboard/src/platform/unix/serv_files.rs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/libs/clipboard/src/platform/unix/serv_files.rs b/libs/clipboard/src/platform/unix/serv_files.rs index 91e456f58..ff7503a33 100644 --- a/libs/clipboard/src/platform/unix/serv_files.rs +++ b/libs/clipboard/src/platform/unix/serv_files.rs @@ -7,6 +7,10 @@ use hbb_common::{ use parking_lot::Mutex; use std::{collections::VecDeque, path::PathBuf, sync::Arc, time::SystemTime, usize}; +// Our clients request at most 4 MiB per range. A larger read is refused, not shortened: +// a short response reads as end of file to IStream callers on Windows. +const MAX_RANGE_READ: u64 = 16 * 1024 * 1024; + lazy_static::lazy_static! { // local files are cached, this value should not be changed when copying files // Because `CliprdrFileContentsRequest` only contains the index of the file in the list. @@ -231,21 +235,16 @@ impl ClipFiles { length }; - // A larger response cannot be framed for sending, so fail before reading it. - if read_size > hbb_common::bytes_codec::MAX_FRAME_LENGTH as u64 { + if read_size > MAX_RANGE_READ { return Err(CliprdrError::InvalidRequest { description: format!( - "file contents request of {} bytes exceeds the frame limit, conn: {}", - read_size, conn_id + "file contents request of {} bytes exceeds the {} byte limit, conn: {}", + read_size, MAX_RANGE_READ, conn_id ), }); } - // The peer picks the size; fail the request, not the process. - let mut buf = Vec::new(); - buf.try_reserve_exact(read_size as usize) - .map_err(|_| CliprdrError::CliprdrOutOfMemory)?; - buf.resize(read_size as usize, 0); + let mut buf = vec![0u8; read_size as usize]; file.read_exact_at(&mut buf, offset)?; @@ -580,10 +579,10 @@ mod sig_test { } #[test] - fn range_request_over_frame_limit_is_rejected() { - let tmp = TmpDir::new("frame"); - let file = tmp.join("huge.bin"); - let size = hbb_common::bytes_codec::MAX_FRAME_LENGTH as u64 + 1; + fn range_request_over_limit_is_rejected() { + let tmp = TmpDir::new("limit"); + let file = tmp.join("big.bin"); + let size = MAX_RANGE_READ + 1; write_at(&file, size - 1, b"x"); let files = vec![path_str(&file)];