summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Shakevsky <shakevsky@berkeley.edu>2026-09-01 00:05:31 +0000
committerNamjae Jeon <linkinjeon@kernel.org>2026-09-02 17:58:15 +0900
commit0480cee8cc3cc906124d398a9779eda144de6b41 (patch)
treec6859c1ce69179b66985c1b3654944ccbfd188b4
parent0e753899627b5e28a9fea8bca98262a6f65a2452 (diff)
ksmbd: validate COPYCHUNK source and target ranges
ksmbd_vfs_copy_file_ranges() rejects negative source offsets in the copy loop, but it does not validate target offsets. It also calculates lock and overlap endpoints before ensuring that either range fits within MAX_LFS_FILESIZE. When the target is an alternate data stream, the buffered path passes a negative target offset to ksmbd_vfs_stream_write(). Let n be Length and let -d be TargetOffset, where 0 < d < n <= XATTR_SIZE_MAX. For an empty stream, the writer allocates n - d bytes, then copies n bytes starting d bytes before the allocation. An authenticated SMB client can control d and the source data, overwrite kernel heap memory, and crash the host. Validate both ranges before lock, overlap, or I/O calculations. Fixes: 8482150a0743 ("ksmbd: support copychunk for alternate data streams") Assisted-by: Antiproof:GPT-5.6-Sol Signed-off-by: Alon Shakevsky <shakevsky@berkeley.edu> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
-rw-r--r--fs/smb/server/vfs.c12
1 files changed, 11 insertions, 1 deletions
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index d2b524f79cbe..c2c9aaa5de1b 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -2007,6 +2007,11 @@ out:
return ret;
}
+static bool ksmbd_vfs_copy_range_valid(loff_t offset, size_t len)
+{
+ return offset >= 0 && (loff_t)len <= MAX_LFS_FILESIZE - offset;
+}
+
int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
struct ksmbd_file *src_fp,
struct ksmbd_file *dst_fp,
@@ -2042,6 +2047,10 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
dst_off = le64_to_cpu(chunks[i].TargetOffset);
len = le32_to_cpu(chunks[i].Length);
+ if (!ksmbd_vfs_copy_range_valid(src_off, len) ||
+ !ksmbd_vfs_copy_range_valid(dst_off, len))
+ return -E2BIG;
+
if (check_lock_range(src_fp->filp, src_off,
src_off + len - 1, READ))
return -EAGAIN;
@@ -2134,7 +2143,8 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
len = le32_to_cpu(chunks[i].Length);
copy_len = len;
- if (src_off < 0)
+ if (!ksmbd_vfs_copy_range_valid(src_off, len) ||
+ !ksmbd_vfs_copy_range_valid(dst_off, len))
return -E2BIG;
if (src_off > src_file_size || len > src_file_size - src_off) {