* [PATCH] ksmbd: fix integer overflow in set_file_allocation_info()
@ 2026-07-09 15:05 Ibrahim Hashimov
2026-07-10 1:25 ` Namjae Jeon
0 siblings, 1 reply; 2+ messages in thread
From: Ibrahim Hashimov @ 2026-07-09 15:05 UTC (permalink / raw)
To: Namjae Jeon, Steve French
Cc: Sergey Senozhatsky, Tom Talpey, linux-cifs, linux-kernel, stable
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.
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Cc: stable@vger.kernel.org
Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
Assisted-by: AuditCode-AI:2026.07
---
fs/smb/server/smb2pdu.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 097f51fc7ed6..324916533669 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -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) {
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] ksmbd: fix integer overflow in set_file_allocation_info()
2026-07-09 15:05 [PATCH] ksmbd: fix integer overflow in set_file_allocation_info() Ibrahim Hashimov
@ 2026-07-10 1:25 ` Namjae Jeon
0 siblings, 0 replies; 2+ messages in thread
From: Namjae Jeon @ 2026-07-10 1:25 UTC (permalink / raw)
To: security
Cc: Steve French, Sergey Senozhatsky, Tom Talpey, linux-cifs,
linux-kernel, stable
On Fri, Jul 10, 2026 at 9:32 AM Ibrahim Hashimov <security@auditcode.ai> wrote:
>
> 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.
>
> Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
> Cc: stable@vger.kernel.org
> Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
> Assisted-by: AuditCode-AI:2026.07
Applied it to #ksmbd-for-next-next.
Thanks!
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-10 1:25 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 15:05 [PATCH] ksmbd: fix integer overflow in set_file_allocation_info() Ibrahim Hashimov
2026-07-10 1:25 ` Namjae Jeon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox