* [PATCH] smb: client: fix heap overflow in cifs_do_set_acl() from mismatched ACL format sizes
@ 2026-08-25 18:32 Frank Sorenson
2026-08-25 21:43 ` [PATCH v2] smb: client: fix heap overflow in cifs_do_set_acl() Frank Sorenson
0 siblings, 1 reply; 7+ messages in thread
From: Frank Sorenson @ 2026-08-25 18:32 UTC (permalink / raw)
To: linux-cifs; +Cc: pc, linkinjeon, stable
cifs_set_acl() validates ACL size using posix_acl_xattr_size(), which
computes the Linux xattr format size:
4 + (count * 8) // 4-byte header + 8 bytes per ACE
But cifs_do_set_acl() calls posix_acl_to_cifs() to write the CIFS
wire format into the same buffer:
6 + (count * 10) // 6-byte header + 10 bytes per ACE
For the same entry count, the CIFS format is ~25% larger. An ACL
with a near-maximum count that passes the xattr-based size check in
cifs_set_acl() can overflow the heap allocation when written by
posix_acl_to_cifs().
Before converting, validate that the CIFS format size fits in the
remaining buffer space. Replace the bogus hardcoded MaxDataCount
value (1000) with CIFSMaxBufSize.
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
fs/smb/client/cifssmb.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index f5aad5f61dce..4ce22fe3cb2e 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count, data_count, param_offset, offset;
+ size_t cifs_acl_size, bytes_available;
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
setAclRetry:
@@ -3575,7 +3576,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
params = 6 + name_len;
pSMB->MaxParameterCount = cpu_to_le16(2);
/* BB find max SMB size from sess */
- pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -3587,6 +3588,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
parm_data = ((char *)pSMB) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ /* make sure we can fit the larger cifs_posix_aces in the buffer */
+ cifs_acl_size = sizeof(struct cifs_posix_acl) +
+ (acl->a_count * sizeof(struct cifs_posix_ace));
+ bytes_available = (CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) - offset;
+ if (cifs_acl_size > bytes_available) {
+ rc = -E2BIG;
+ goto setACLerrorExit;
+ }
+
/* convert to on the wire format for POSIX ACL */
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2] smb: client: fix heap overflow in cifs_do_set_acl()
2026-08-25 18:32 [PATCH] smb: client: fix heap overflow in cifs_do_set_acl() from mismatched ACL format sizes Frank Sorenson
@ 2026-08-25 21:43 ` Frank Sorenson
2026-08-26 0:03 ` Paulo Alcantara
0 siblings, 1 reply; 7+ messages in thread
From: Frank Sorenson @ 2026-08-25 21:43 UTC (permalink / raw)
To: linux-cifs; +Cc: pc, linkinjeon, stable
cifs_set_acl() validates ACL size using posix_acl_xattr_size():
4 + (count * 8) // 4-byte header + 8 bytes per ACE
cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS
wire format into the same buffer:
6 + (count * 10) // 6-byte header + 10 bytes per ACE
An ACL that passes the xattr-based check in cifs_set_acl() can
overflow the heap when posix_acl_to_cifs() writes the larger CIFS
format.
Validate the CIFS format size against the remaining buffer space and
USHRT_MAX before converting--data_count is __u16, so sizes above
USHRT_MAX truncate the on-wire packet length, causing the server to
apply a partial ACL. Replace MaxDataCount = 1000 with
min(CIFSMaxBufSize, USHRT_MAX).
Fixes: dc1af4c4b4721 ("cifs: implement set acl method")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
v2 changes:
- Add USHRT_MAX bound to prevent u16 truncation of data_count for ACLs
with more than 6553 entries, which would cause a partial ACL to be
silently applied on the server
- limit MaxDataCount to min(CIFSMaxBufSize, USHRT_MAX)
fs/smb/client/cifssmb.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index f5aad5f61dce..621aca5d3b75 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count, data_count, param_offset, offset;
+ size_t cifs_acl_size, bytes_available;
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
setAclRetry:
@@ -3574,8 +3575,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
}
params = 6 + name_len;
pSMB->MaxParameterCount = cpu_to_le16(2);
- /* BB find max SMB size from sess */
- pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -3587,6 +3587,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
parm_data = ((char *)pSMB) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ /* make sure we can fit the larger cifs_posix_aces in the buffer */
+ cifs_acl_size = sizeof(struct cifs_posix_acl) +
+ (acl->a_count * sizeof(struct cifs_posix_ace));
+ bytes_available = (CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) - offset;
+ if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
+ rc = -E2BIG;
+ goto setACLerrorExit;
+ }
+
/* convert to on the wire format for POSIX ACL */
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2] smb: client: fix heap overflow in cifs_do_set_acl()
2026-08-25 21:43 ` [PATCH v2] smb: client: fix heap overflow in cifs_do_set_acl() Frank Sorenson
@ 2026-08-26 0:03 ` Paulo Alcantara
2026-08-26 1:47 ` Frank Sorenson
0 siblings, 1 reply; 7+ messages in thread
From: Paulo Alcantara @ 2026-08-26 0:03 UTC (permalink / raw)
To: Frank Sorenson, linux-cifs; +Cc: linkinjeon, stable
Frank Sorenson <sorenson@redhat.com> writes:
> cifs_set_acl() validates ACL size using posix_acl_xattr_size():
>
> 4 + (count * 8) // 4-byte header + 8 bytes per ACE
>
> cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS
> wire format into the same buffer:
>
> 6 + (count * 10) // 6-byte header + 10 bytes per ACE
>
> An ACL that passes the xattr-based check in cifs_set_acl() can
> overflow the heap when posix_acl_to_cifs() writes the larger CIFS
> format.
>
> Validate the CIFS format size against the remaining buffer space and
> USHRT_MAX before converting--data_count is __u16, so sizes above
> USHRT_MAX truncate the on-wire packet length, causing the server to
> apply a partial ACL. Replace MaxDataCount = 1000 with
> min(CIFSMaxBufSize, USHRT_MAX).
>
> Fixes: dc1af4c4b4721 ("cifs: implement set acl method")
> Cc: stable@vger.kernel.org
> Signed-off-by: Frank Sorenson <sorenson@redhat.com>
> ---
> v2 changes:
> - Add USHRT_MAX bound to prevent u16 truncation of data_count for ACLs
> with more than 6553 entries, which would cause a partial ACL to be
> silently applied on the server
> - limit MaxDataCount to min(CIFSMaxBufSize, USHRT_MAX)
>
> fs/smb/client/cifssmb.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
> index f5aad5f61dce..621aca5d3b75 100644
> --- a/fs/smb/client/cifssmb.c
> +++ b/fs/smb/client/cifssmb.c
> @@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
> int rc = 0;
> int bytes_returned = 0;
> __u16 params, byte_count, data_count, param_offset, offset;
> + size_t cifs_acl_size, bytes_available;
>
> cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
> setAclRetry:
> @@ -3574,8 +3575,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
> }
> params = 6 + name_len;
> pSMB->MaxParameterCount = cpu_to_le16(2);
> - /* BB find max SMB size from sess */
> - pSMB->MaxDataCount = cpu_to_le16(1000);
> + pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
> pSMB->MaxSetupCount = 0;
> pSMB->Reserved = 0;
> pSMB->Flags = 0;
> @@ -3587,6 +3587,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
> parm_data = ((char *)pSMB) + offset;
> pSMB->ParameterOffset = cpu_to_le16(param_offset);
>
> + /* make sure we can fit the larger cifs_posix_aces in the buffer */
> + cifs_acl_size = sizeof(struct cifs_posix_acl) +
> + (acl->a_count * sizeof(struct cifs_posix_ace));
> + bytes_available = (CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) - offset;
Are you sure you want to use MAX_SMB2_HDR_SIZE? This is SMB1 code, so I
would expect to see MAX_CIFS_HDR_SIZE. Alternatively, use
MAX_HEADER_SIZE() helper.
Do you have any reproducer?
What server did you test these changes against?
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2] smb: client: fix heap overflow in cifs_do_set_acl()
2026-08-26 0:03 ` Paulo Alcantara
@ 2026-08-26 1:47 ` Frank Sorenson
2026-08-26 22:52 ` Paulo Alcantara
0 siblings, 1 reply; 7+ messages in thread
From: Frank Sorenson @ 2026-08-26 1:47 UTC (permalink / raw)
To: Paulo Alcantara, linux-cifs; +Cc: linkinjeon
[-- Attachment #1: Type: text/plain, Size: 6382 bytes --]
On 8/25/26 7:03 PM, Paulo Alcantara wrote:
> Frank Sorenson <sorenson@redhat.com> writes:
>
>> cifs_set_acl() validates ACL size using posix_acl_xattr_size():
>>
>> 4 + (count * 8) // 4-byte header + 8 bytes per ACE
>>
>> cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS
>> wire format into the same buffer:
>>
>> 6 + (count * 10) // 6-byte header + 10 bytes per ACE
>>
>> An ACL that passes the xattr-based check in cifs_set_acl() can
>> overflow the heap when posix_acl_to_cifs() writes the larger CIFS
>> format.
>>
>> Validate the CIFS format size against the remaining buffer space and
>> USHRT_MAX before converting--data_count is __u16, so sizes above
>> USHRT_MAX truncate the on-wire packet length, causing the server to
>> apply a partial ACL. Replace MaxDataCount = 1000 with
>> min(CIFSMaxBufSize, USHRT_MAX).
>>
>> Fixes: dc1af4c4b4721 ("cifs: implement set acl method")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Frank Sorenson <sorenson@redhat.com>
>> ---
>> v2 changes:
>> - Add USHRT_MAX bound to prevent u16 truncation of data_count for ACLs
>> with more than 6553 entries, which would cause a partial ACL to be
>> silently applied on the server
>> - limit MaxDataCount to min(CIFSMaxBufSize, USHRT_MAX)
>>
>> fs/smb/client/cifssmb.c | 13 +++++++++++--
>> 1 file changed, 11 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
>> index f5aad5f61dce..621aca5d3b75 100644
>> --- a/fs/smb/client/cifssmb.c
>> +++ b/fs/smb/client/cifssmb.c
>> @@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
>> int rc = 0;
>> int bytes_returned = 0;
>> __u16 params, byte_count, data_count, param_offset, offset;
>> + size_t cifs_acl_size, bytes_available;
>>
>> cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
>> setAclRetry:
>> @@ -3574,8 +3575,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
>> }
>> params = 6 + name_len;
>> pSMB->MaxParameterCount = cpu_to_le16(2);
>> - /* BB find max SMB size from sess */
>> - pSMB->MaxDataCount = cpu_to_le16(1000);
>> + pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
>> pSMB->MaxSetupCount = 0;
>> pSMB->Reserved = 0;
>> pSMB->Flags = 0;
>> @@ -3587,6 +3587,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
>> parm_data = ((char *)pSMB) + offset;
>> pSMB->ParameterOffset = cpu_to_le16(param_offset);
>>
>> + /* make sure we can fit the larger cifs_posix_aces in the buffer */
>> + cifs_acl_size = sizeof(struct cifs_posix_acl) +
>> + (acl->a_count * sizeof(struct cifs_posix_ace));
>> + bytes_available = (CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) - offset;
> Are you sure you want to use MAX_SMB2_HDR_SIZE? This is SMB1 code, so I
> would expect to see MAX_CIFS_HDR_SIZE. Alternatively, use
> MAX_HEADER_SIZE() helper.
The only reason I used MAX_SMB2_HDR_SIZE is that it's the actual size
allocated in cifs_buf_get() from cifs_req_cachep... but now that I
think about it, we don't actually want to use the extra
(MAX_SMB2_HDR_SIZE - MAX_CIFS_HDR_SIZE) bytes, even though we have it
allocated. So as you say, MAX_CIFS_HDR_SIZE or
MAX_HEADER_SIZE(tcon->ses->server) would be better.
I can respin.
> Do you have any reproducer?
Yes, I'll attach the script. It sets 1800 named user ACEs, which is
14436 xattr bytes, ~18046 CIFS bytes
7.2-ish unpatched:
# mount //vm25/user1 /mnt/vm25 -overs=1.0,username=user1,password=pass,unix
# python3 reproduce_acl_overflow.py /mnt/vm25/testfile
Setting ACL with 1800 named user ACEs (14436 xattr bytes, ~18046 CIFS bytes)
Unpatched kernel: expect KASAN report or silent heap corruption
Patched kernel: expect -E2BIG from setxattr
setxattr failed: [Errno 5] Input/output error: '/mnt/vm25/testfile'
[ 1068.599600] BUG: KASAN: slab-out-of-bounds in
posix_acl_to_cifs+0x60a/0x6b0 [cifs]
[ 1068.599952] Write of size 8 at addr ffff88810625c0ca by task
python3/40835
[ 1068.600017] CPU: 2 UID: 0 PID: 40835 Comm: python3 Not tainted
7.2.0-rc7+ #6 PREEMPT(full)
[ 1068.600029] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS
1.17.0-10.fc44 06/10/2025
[ 1068.600041] Call Trace:
[ 1068.600047] <TASK>
[ 1068.600050] dump_stack_lvl+0x4e/0x70
[ 1068.600066] ? posix_acl_to_cifs+0x60a/0x6b0 [cifs]
[ 1068.600312] print_address_description.constprop.0+0x70/0x300
[ 1068.600324] ? posix_acl_to_cifs+0x60a/0x6b0 [cifs]
[ 1068.600570] print_report+0x108/0x209
[ 1068.600580] ? posix_acl_to_cifs+0x60a/0x6b0 [cifs]
[ 1068.600818] kasan_report+0xf0/0x120
[ 1068.600827] ? posix_acl_to_cifs+0x60a/0x6b0 [cifs]
[ 1068.601059] posix_acl_to_cifs+0x60a/0x6b0 [cifs]
[ 1068.601292] cifs_do_set_acl+0x446/0xb90 [cifs]
...
[ 1068.616380] Allocated by task 40835:
[ 1068.617021] kasan_save_stack+0x30/0x50
[ 1068.617033] kasan_save_track+0x14/0x30
[ 1068.617039] __kasan_slab_alloc+0x89/0x90
[ 1068.617045] kmem_cache_alloc_noprof+0x155/0x430
[ 1068.617053] mempool_alloc_noprof+0x11b/0x1e0
[ 1068.617062] cifs_buf_get+0x36/0x90 [cifs]
[ 1068.617344] smb_init+0x43/0x100 [cifs]
[ 1068.617618] cifs_do_set_acl+0x147/0xb90 [cifs]
[ 1068.617860] cifs_set_acl+0x717/0x920 [cifs]
[ 1068.618096] vfs_set_acl+0x35a/0x880
[ 1068.618104] do_set_acl+0xa6/0x160
[ 1068.618109] filename_setxattr+0x129/0x170
[ 1068.618115] path_setxattrat+0x156/0x290
[ 1068.618120] __x64_sys_setxattr+0xc6/0x140
[ 1068.618125] do_syscall_64+0xe3/0x540
[ 1068.618133] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 1068.618793] The buggy address belongs to the object at ffff888106258000
which belongs to the cache cifs_request of size 16588
[ 1068.619990] The buggy address is located 16586 bytes inside of
allocated 16588-byte region [ffff888106258000,
ffff88810625c0cc)
7.2 patched
# python3 reproduce_acl_overflow.py /mnt/vm25/testfile Setting ACL with
1800 named user ACEs (14436 xattr bytes, ~18046 CIFS bytes) Unpatched
kernel: expect KASAN report or silent heap corruption Patched kernel:
expect -E2BIG from setxattr Got -E2BIG -- fix is active
> What server did you test these changes against?
samba
--
Frank Sorenson
sorenson@redhat.com
Principal Software Maintenance Engineer, filesystems
Red Hat
[-- Attachment #2: reproduce_acl_overflow.py --]
[-- Type: text/x-python, Size: 2396 bytes --]
#!/usr/bin/env python3
"""
Reproducer for cifs_do_set_acl() heap overflow.
cifs_set_acl() validates ACL size using Linux xattr format (8 bytes/ACE),
but posix_acl_to_cifs() writes CIFS wire format (10 bytes/ACE) into the
same buffer. With CIFSMaxBufSize=16384 (default), overflow zone is
~1650-2047 named user ACEs.
Requires Samba share mounted with Unix extensions:
mount -t cifs //server/share /mnt -o unix,username=user,password=pass
Usage: python3 reproduce_acl_overflow.py <path_on_cifs_mount>
"""
import os, sys, struct
POSIX_ACL_XATTR_VERSION = 2
ACL_USER_OBJ = 0x0001
ACL_USER = 0x0002
ACL_GROUP_OBJ = 0x0004
ACL_MASK = 0x0010
ACL_OTHER = 0x0020
ACL_READ = 0x04
def build_acl(n_user_aces):
"""Build a valid POSIX ACL xattr blob with n_user_aces named user entries."""
entries = [(ACL_USER_OBJ, ACL_READ, 0xffffffff)]
for uid in range(1, n_user_aces + 1):
entries.append((ACL_USER, ACL_READ, uid))
entries += [
(ACL_GROUP_OBJ, ACL_READ, 0xffffffff),
(ACL_MASK, ACL_READ, 0xffffffff),
(ACL_OTHER, 0, 0xffffffff),
]
blob = struct.pack("<I", POSIX_ACL_XATTR_VERSION)
for tag, perm, uid in entries:
blob += struct.pack("<HHI", tag, perm, uid)
return blob
def main():
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <path_on_cifs_mount>")
sys.exit(1)
path = sys.argv[1]
if not os.path.exists(path):
open(path, 'w').close()
# 1800 ACEs: comfortably in the overflow zone for default CIFSMaxBufSize=16384
# xattr size: 4 + 1804*8 = 14436 bytes (passes cifs_set_acl check)
# cifs size: 6 + 1804*10 = 18046 bytes (overflows ~16588-byte buffer)
n_aces = 1800
blob = build_acl(n_aces)
print(f"Setting ACL with {n_aces} named user ACEs "
f"({len(blob)} xattr bytes, ~{6 + (n_aces + 4) * 10} CIFS bytes)")
print("Unpatched kernel: expect KASAN report or silent heap corruption")
print("Patched kernel: expect -E2BIG from setxattr")
try:
os.setxattr(path, "system.posix_acl_access", blob)
print("setxattr succeeded (unexpected -- server accepted it)")
except OSError as e:
import errno
if e.errno == errno.E2BIG:
print("Got -E2BIG -- fix is active")
else:
print(f"setxattr failed: {e}")
if __name__ == "__main__":
main()
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2] smb: client: fix heap overflow in cifs_do_set_acl()
2026-08-26 1:47 ` Frank Sorenson
@ 2026-08-26 22:52 ` Paulo Alcantara
2026-08-27 2:13 ` [PATCH v4] " Frank Sorenson
0 siblings, 1 reply; 7+ messages in thread
From: Paulo Alcantara @ 2026-08-26 22:52 UTC (permalink / raw)
To: sorenson, linux-cifs; +Cc: linkinjeon
Frank Sorenson <sorenson@redhat.com> writes:
> On 8/25/26 7:03 PM, Paulo Alcantara wrote:
>> Frank Sorenson <sorenson@redhat.com> writes:
>>
>>> cifs_set_acl() validates ACL size using posix_acl_xattr_size():
>>>
>>> 4 + (count * 8) // 4-byte header + 8 bytes per ACE
>>>
>>> cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS
>>> wire format into the same buffer:
>>>
>>> 6 + (count * 10) // 6-byte header + 10 bytes per ACE
>>>
>>> An ACL that passes the xattr-based check in cifs_set_acl() can
>>> overflow the heap when posix_acl_to_cifs() writes the larger CIFS
>>> format.
>>>
>>> Validate the CIFS format size against the remaining buffer space and
>>> USHRT_MAX before converting--data_count is __u16, so sizes above
>>> USHRT_MAX truncate the on-wire packet length, causing the server to
>>> apply a partial ACL. Replace MaxDataCount = 1000 with
>>> min(CIFSMaxBufSize, USHRT_MAX).
>>>
>>> Fixes: dc1af4c4b4721 ("cifs: implement set acl method")
>>> Cc: stable@vger.kernel.org
>>> Signed-off-by: Frank Sorenson <sorenson@redhat.com>
>>> ---
>>> v2 changes:
>>> - Add USHRT_MAX bound to prevent u16 truncation of data_count for ACLs
>>> with more than 6553 entries, which would cause a partial ACL to be
>>> silently applied on the server
>>> - limit MaxDataCount to min(CIFSMaxBufSize, USHRT_MAX)
>>>
>>> fs/smb/client/cifssmb.c | 13 +++++++++++--
>>> 1 file changed, 11 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
>>> index f5aad5f61dce..621aca5d3b75 100644
>>> --- a/fs/smb/client/cifssmb.c
>>> +++ b/fs/smb/client/cifssmb.c
>>> @@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
>>> int rc = 0;
>>> int bytes_returned = 0;
>>> __u16 params, byte_count, data_count, param_offset, offset;
>>> + size_t cifs_acl_size, bytes_available;
>>>
>>> cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
>>> setAclRetry:
>>> @@ -3574,8 +3575,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
>>> }
>>> params = 6 + name_len;
>>> pSMB->MaxParameterCount = cpu_to_le16(2);
>>> - /* BB find max SMB size from sess */
>>> - pSMB->MaxDataCount = cpu_to_le16(1000);
>>> + pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
>>> pSMB->MaxSetupCount = 0;
>>> pSMB->Reserved = 0;
>>> pSMB->Flags = 0;
>>> @@ -3587,6 +3587,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
>>> parm_data = ((char *)pSMB) + offset;
>>> pSMB->ParameterOffset = cpu_to_le16(param_offset);
>>>
>>> + /* make sure we can fit the larger cifs_posix_aces in the buffer */
>>> + cifs_acl_size = sizeof(struct cifs_posix_acl) +
>>> + (acl->a_count * sizeof(struct cifs_posix_ace));
>>> + bytes_available = (CIFSMaxBufSize + MAX_SMB2_HDR_SIZE) - offset;
>> Are you sure you want to use MAX_SMB2_HDR_SIZE? This is SMB1 code, so I
>> would expect to see MAX_CIFS_HDR_SIZE. Alternatively, use
>> MAX_HEADER_SIZE() helper.
>
> The only reason I used MAX_SMB2_HDR_SIZE is that it's the actual size
> allocated in cifs_buf_get() from cifs_req_cachep... but now that I
> think about it, we don't actually want to use the extra
> (MAX_SMB2_HDR_SIZE - MAX_CIFS_HDR_SIZE) bytes, even though we have it
> allocated. So as you say, MAX_CIFS_HDR_SIZE or
> MAX_HEADER_SIZE(tcon->ses->server) would be better.
Thanks for checking.
> I can respin.
Please.
>> Do you have any reproducer?
>
> Yes, I'll attach the script. It sets 1800 named user ACEs, which is
> 14436 xattr bytes, ~18046 CIFS bytes
Thanks. That's really useful.
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4] smb: client: fix heap overflow in cifs_do_set_acl()
2026-08-26 22:52 ` Paulo Alcantara
@ 2026-08-27 2:13 ` Frank Sorenson
2026-08-28 1:18 ` Paulo Alcantara
0 siblings, 1 reply; 7+ messages in thread
From: Frank Sorenson @ 2026-08-27 2:13 UTC (permalink / raw)
To: linux-cifs; +Cc: pc, linkinjeon, stable
cifs_set_acl() validates ACL size using posix_acl_xattr_size():
4 + (count * 8) // 4-byte header + 8 bytes per ACE
cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS
wire format into the same buffer:
6 + (count * 10) // 6-byte header + 10 bytes per ACE
An ACL that passes the xattr-based check in cifs_set_acl() can
overflow the heap when posix_acl_to_cifs() writes the larger CIFS
format.
Validate the CIFS format size against the remaining buffer space and
USHRT_MAX before converting--data_count is __u16, so sizes above
USHRT_MAX truncate the on-wire packet length, causing the server to
apply a partial ACL. Replace MaxDataCount = 1000 with
min(CIFSMaxBufSize, USHRT_MAX).
Fixes: dc1af4c4b4721 ("cifs: implement set acl method")
Cc: stable@vger.kernel.org
Signed-off-by: Frank Sorenson <sorenson@redhat.com>
---
reproducer script which sets 1800 named user ACEs will overflow,
throwing KASAN error or silently corrupting heap
v3/4 changes:
- change header size to use MAX_HEADER_SIZE helper
v2 changes:
- Add USHRT_MAX bound to prevent u16 truncation of data_count for ACLs
with more than 6553 entries, which would cause a partial ACL to be
silently applied on the server
- limit MaxDataCount to min(CIFSMaxBufSize, USHRT_MAX)
fs/smb/client/cifssmb.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index f5aad5f61dce..230af243247c 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count, data_count, param_offset, offset;
+ size_t cifs_acl_size, bytes_available;
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
setAclRetry:
@@ -3574,8 +3575,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
}
params = 6 + name_len;
pSMB->MaxParameterCount = cpu_to_le16(2);
- /* BB find max SMB size from sess */
- pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -3587,6 +3587,15 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
parm_data = ((char *)pSMB) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ /* make sure we can fit the larger cifs_posix_aces in the buffer */
+ cifs_acl_size = sizeof(struct cifs_posix_acl) +
+ (acl->a_count * sizeof(struct cifs_posix_ace));
+ bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset;
+ if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
+ rc = -E2BIG;
+ goto setACLerrorExit;
+ }
+
/* convert to on the wire format for POSIX ACL */
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4] smb: client: fix heap overflow in cifs_do_set_acl()
2026-08-27 2:13 ` [PATCH v4] " Frank Sorenson
@ 2026-08-28 1:18 ` Paulo Alcantara
0 siblings, 0 replies; 7+ messages in thread
From: Paulo Alcantara @ 2026-08-28 1:18 UTC (permalink / raw)
To: Frank Sorenson, linux-cifs; +Cc: linkinjeon, stable
Frank Sorenson <sorenson@redhat.com> writes:
> cifs_set_acl() validates ACL size using posix_acl_xattr_size():
>
> 4 + (count * 8) // 4-byte header + 8 bytes per ACE
>
> cifs_do_set_acl() then calls posix_acl_to_cifs() to write the CIFS
> wire format into the same buffer:
>
> 6 + (count * 10) // 6-byte header + 10 bytes per ACE
>
> An ACL that passes the xattr-based check in cifs_set_acl() can
> overflow the heap when posix_acl_to_cifs() writes the larger CIFS
> format.
>
> Validate the CIFS format size against the remaining buffer space and
> USHRT_MAX before converting--data_count is __u16, so sizes above
> USHRT_MAX truncate the on-wire packet length, causing the server to
> apply a partial ACL. Replace MaxDataCount = 1000 with
> min(CIFSMaxBufSize, USHRT_MAX).
> ...
Applied.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-28 1:18 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25 18:32 [PATCH] smb: client: fix heap overflow in cifs_do_set_acl() from mismatched ACL format sizes Frank Sorenson
2026-08-25 21:43 ` [PATCH v2] smb: client: fix heap overflow in cifs_do_set_acl() Frank Sorenson
2026-08-26 0:03 ` Paulo Alcantara
2026-08-26 1:47 ` Frank Sorenson
2026-08-26 22:52 ` Paulo Alcantara
2026-08-27 2:13 ` [PATCH v4] " Frank Sorenson
2026-08-28 1:18 ` Paulo Alcantara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox