mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-10-21 04:23:48 +00:00
fix(clipboard): unix file server, bound range reads
The Range request fields nPositionLow and cbRequested are UINT32s carried in i32 fields, but were sign-extended to u64. A negative cbRequested became a near-u64::MAX length, `offset + length` wrapped past the clamp, and the server tried to allocate a u64::MAX buffer, aborting the process. Sign-extending nPositionLow also rejected offsets whose low word is >= 2 GiB, breaking reads past 2 GiB in large files. - decode nPositionLow and cbRequested as u32 - clamp with `length > file.size - offset` (offset <= size is checked above) - allocate the read buffer fallibly and return an error instead of aborting Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
58ff78a823
commit
cccf9019e0
@@ -213,13 +213,23 @@ impl ClipFiles {
|
||||
),
|
||||
});
|
||||
}
|
||||
let read_size = if offset + length > file.size {
|
||||
let read_size = if length > file.size - offset {
|
||||
file.size - offset
|
||||
} else {
|
||||
length
|
||||
};
|
||||
|
||||
let mut buf = vec![0u8; read_size as usize];
|
||||
// The peer picks the size (up to 4 GiB); fail the request, not the process.
|
||||
let mut buf = Vec::new();
|
||||
if buf.try_reserve_exact(read_size as usize).is_err() {
|
||||
log::error!(
|
||||
"failed to allocate {} bytes for file contents requested from conn: {}",
|
||||
read_size,
|
||||
conn_id
|
||||
);
|
||||
return Err(CliprdrError::CliprdrOutOfMemory);
|
||||
}
|
||||
buf.resize(read_size as usize, 0);
|
||||
|
||||
file.read_exact_at(&mut buf, offset)?;
|
||||
|
||||
@@ -266,8 +276,11 @@ pub fn read_file_contents(
|
||||
file_idx: list_index as usize,
|
||||
}
|
||||
} else if dw_flags == 0x2 {
|
||||
let offset = (n_position_high as u64) << 32 | n_position_low as u64;
|
||||
let length = cb_requested as u64;
|
||||
// nPositionLow and cbRequested are UINT32s carried in i32 fields. Sign-extending
|
||||
// them rejects offsets whose low word is >= 2 GiB and turns a negative length
|
||||
// into a near-u64::MAX read.
|
||||
let offset = (n_position_high as u64) << 32 | n_position_low as u32 as u64;
|
||||
let length = cb_requested as u32 as u64;
|
||||
|
||||
FileContentsRequest::Range {
|
||||
stream_id,
|
||||
@@ -415,4 +428,35 @@ mod sig_test {
|
||||
|
||||
clear_files(); // leave the global clean for other tests
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range_request_huge_length_is_clamped() {
|
||||
let tmp = TmpDir::new("range");
|
||||
let file = tmp.join("data.bin");
|
||||
fs::write(&file, b"0123456789").unwrap();
|
||||
let files = vec![path_str(&file)];
|
||||
|
||||
let mut clip = ClipFiles::default();
|
||||
clip.sync_files(&files, fingerprint(&files)).unwrap();
|
||||
let file_idx = clip.first_file_index;
|
||||
|
||||
// offset + length used to wrap to 0, skip the clamp and ask for a u64::MAX buffer.
|
||||
let resp = clip
|
||||
.serve_file_contents(
|
||||
0,
|
||||
FileContentsRequest::Range {
|
||||
stream_id: 0,
|
||||
file_idx,
|
||||
offset: 1,
|
||||
length: u64::MAX,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
match resp {
|
||||
ClipboardFile::FileContentsResponse { requested_data, .. } => {
|
||||
assert_eq!(requested_data, b"123456789");
|
||||
}
|
||||
_ => panic!("unexpected response"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user