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) <noreply@anthropic.com>
This commit is contained in:
rustdeskandClaude Opus 5.5 committed 2026-09-24 15:05:54 +08:00
1 parent a93d0756ff
commit daff54387b
1 file changed
+12 -13
+12 -13
View File
@@ -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)];