ksmbd: fix integer overflow in set_file_allocation_info()
Subsystems (3)
PATCH 1/1 ksmbd: fix integer overflow in set_file_allocation_info()
set_file_allocation_info() converts the client-supplied FILE_ALLOCATION_INFORMATION::AllocationSize into a 512-byte block count with:
alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;
AllocationSize is a fully client-controlled __le64 field; the only validation performed by the caller (smb2_set_info_file(), case FILE_ALLOCATION_INFORMATION) is that the fixed buffer is at least sizeof(struct smb2_file_alloc_info) == 8 bytes. The value itself is never range-checked before this arithmetic.
When AllocationSize is close to U64_MAX (e.g. 0xffffffffffffffff), "AllocationSize + 511" wraps around mod 2^64 to a small number (0xffffffffffffffff + 511 = 510), so alloc_blks becomes 0. Since any existing regular file has stat.blocks > 0, the function then takes the "shrink" branch and calls:
ksmbd_vfs_truncate(work, fp, alloc_blks * 512); /* == 0 */
silently truncating the file to size 0, even though the client asked to grow the allocation to (what looks like) the maximum possible size. The trailing "if (size < alloc_blks * 512) i_size_write(inode, size);" restore is guarded by a comparison that is never true once alloc_blks == 0, so the truncation is not undone. This lets an authenticated SMB client that already holds an open handle with FILE_WRITE_DATA on a file silently truncate that same file to size 0 via a single crafted SET_INFO(FILE_ALLOCATION_INFORMATION) request advertising a near-U64_MAX AllocationSize, even though the request asks to grow the file's allocation rather than shrink it. This is a functional/data-loss bug, not a privilege-boundary
violation: the same client could already truncate the file via FILE_END_OF_FILE_INFORMATION or a plain write.
Fix it by validating AllocationSize against MAX_LFS_FILESIZE, the same upper bound the VFS itself uses to reject unrepresentable file sizes, before doing the "+511" rounding, and rejecting oversized values with -EINVAL. Bounding AllocationSize to MAX_LFS_FILESIZE - 511 guarantees the "+511" addition cannot wrap, and that the subsequent "alloc_blks * 512" values passed to vfs_fallocate() and ksmbd_vfs_truncate() stay within a representable loff_t as well.
No legitimate SMB client asks for an allocation size anywhere near 2^64 bytes, so this only rejects a value that was previously silently misinterpreted as zero.
Runtime-verified on a v6.19 KASAN test stand: sending SET_INFO (FILE_ALLOCATION_INFORMATION) with AllocationSize = 0xffffffffffffffff against ksmbd now returns -EINVAL and leaves the target file's size unchanged, where the unpatched kernel truncated it from 4096 to 0 bytes.
--- fs/smb/server/smb2pdu.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
fs/smb/server/smb2pdu.c +14 -1
@@ -6690,6 +6690,7 @@ static int set_file_allocation_info(struct ksmbd_work *work,*/ loff_t alloc_blks; +u64 alloc_size;struct inode *inode;struct kstat stat;int rc;
@@ -6705,7 +6706,19 @@ static int set_file_allocation_info(struct ksmbd_work *work,if (rc)return rc; - alloc_blks = (le64_to_cpu (file_alloc_info->AllocationSize) +511 ) >>9 ; +/* +* AllocationSize is fully client-controlled (the caller only +* validates the fixed 8-byte buffer length). Reject values that +* would overflow the "round up to 512-byte blocks" conversion +* below instead of silently wrapping it to a tiny block count, +* which would truncate the file to a size the client never +* asked for. +*/ + alloc_size =le64_to_cpu (file_alloc_info->AllocationSize); +if (alloc_size >MAX_LFS_FILESIZE -511 ) +return -EINVAL ; + + alloc_blks = (alloc_size +511 ) >>9 ; inode =file_inode (fp->filp);if (alloc_blks > stat.blocks) {
Signature · 1 line
2.50.1 (Apple Git-155)
Quoted 54 lines · Ibrahim Hashimov
set_file_allocation_info()converts the client-suppliedFILE_ALLOCATION_INFORMATION::AllocationSizeinto a 512-byte block count with:alloc_blks = (le64_to_cpu(file_alloc_info->AllocationSize) + 511) >> 9;AllocationSize is a fully client-controlled
__le64field; the only validation performed by the caller (smb2_set_info_file(), caseFILE_ALLOCATION_INFORMATION) is that the fixed buffer is at leastsizeof(struct smb2_file_alloc_info)== 8 bytes. The value itself is never range-checked before this arithmetic.When AllocationSize is close to
U64_MAX(e.g.0xffffffffffffffff), "AllocationSize + 511" wraps around mod 2^64 to a small number (0xffffffffffffffff+ 511 = 510), soalloc_blksbecomes 0. Since any existing regular file has stat.blocks > 0, the function then takes the "shrink" branch and calls:ksmbd_vfs_truncate(work, fp, alloc_blks * 512); /* == 0 */silently truncating the file to size 0, even though the client asked to grow the allocation to (what looks like) the maximum possible size. The trailing "if (size <
alloc_blks* 512)i_size_write(inode, size);" restore is guarded by a comparison that is never true oncealloc_blks== 0, so the truncation is not undone. This lets an authenticated SMB client that already holds an open handle withFILE_WRITE_DATAon a file silently truncate that same file to size 0 via a single craftedSET_INFO(FILE_ALLOCATION_INFORMATION)request advertising a near-U64_MAX AllocationSize, even though the request asks to grow the file's allocation rather than shrink it. This is a functional/data-loss bug, not a privilege-boundary
violation: the same client could already truncate the file viaFILE_END_OF_FILE_INFORMATIONor a plain write.Fix it by validating AllocationSize against
MAX_LFS_FILESIZE, the same upper bound the VFS itself uses to reject unrepresentable file sizes, before doing the "+511" rounding, and rejecting oversized values with-EINVAL. Bounding AllocationSize toMAX_LFS_FILESIZE- 511 guarantees the "+511" addition cannot wrap, and that the subsequent "alloc_blks* 512" values passed tovfs_fallocate()andksmbd_vfs_truncate()stay within a representableloff_tas well.No legitimate SMB client asks for an allocation size anywhere near 2^64 bytes, so this only rejects a value that was previously silently misinterpreted as zero.
Runtime-verified on a v6.19 KASAN test stand: sending
SET_INFO(FILE_ALLOCATION_INFORMATION) with AllocationSize =0xffffffffffffffffagainst ksmbd now returns-EINVALand leaves the target file's size unchanged, where the unpatched kernel truncated it from 4096 to 0 bytes.
Quoted 4 lines
Fixes e2f34481b24d ("cifsd: add server-side procedures for SMB3")Signed-off-by Ibrahim Hashimov <[email protected]>Assisted-by AuditCode-AI:2026.07
Applied it to #ksmbd-for-next-next.
Thanks!