In the Linux kernel, the following vulnerability has been resolved:
ksmbd: fix use-after-free of a deferred filelock on double SMB2CANCEL
A deferred byte-range lock (an SMB2LOCK that blocks) registers an async work on conn->asyncrequests via setupasyncwork(), with cancelfn = smb2removeblockedlock and cancelargv[0] pointing at the struct filelock.
When the request is cancelled, the worker frees the filelock with locksfreelock() and takes the cancelled early-exit, which "goto out"s and never reaches releaseasyncwork() -- the only site that unlinks the work from conn->asyncrequests and clears cancelfn/cancelargv. The work therefore stays matchable on asyncrequests with a live cancelfn pointing at the freed filelock, until connection teardown finally runs releaseasyncwork().
smb2cancel() fires cancelfn unconditionally with no state guard, so a second SMB2CANCEL for the same AsyncId, arriving in that window, re-runs smb2removeblockedlock() on the freed filelock -- a slab use-after-free:
BUG: KASAN: slab-use-after-free in locksdeleteblock locksdeleteblock locksdeleteblock ksmbdvfsposixlockunblock smb2removeblockedlock smb2cancel <- 2nd SMB2CANCEL fires cancelfn handleksmbdwork Allocated by ...: locksalloclock <- smb2lock Freed by ...: locksfreelock <- smb2lock (cancelled branch) ... cache filelockcache of size 192
Reproduced on mainline with KASAN by an authenticated SMB client.
Skip a work whose state is already KSMBDWORKCANCELLED so its cancel callback cannot be fired a second time.
In the Linux kernel, the following vulnerability has been resolved:
ksmbd: require minimum ACE size in smbcheckpermdacl()
Both ACE-walk loops in smbcheckpermdacl() only guard against an under-sized remaining buffer, not against an ACE whose declared ace->size is smaller than the struct it claims to describe:
if (offsetof(struct smbace, accessreq) > acessize) break; acesize = le16tocpu(ace->size); if (acesize > acessize) break;
The first check only requires the 4-byte ACE header to be in bounds; it does not require accessreq (4 bytes at offset 4) to be readable. An attacker who has set a crafted DACL on a file they own can declare ace->size == 4 with acessize == 4, pass both checks, and then
granted |= le32tocpu(ace->accessreq); / upper loop / comparesids(&sid, &ace->sid); / lower loop /
reads accessreq at offset 4 (OOB by up to 4 bytes) and ace->sid at offset 8 (OOB by up to CIFSSIDBASESIZE + SIDMAXSUBAUTHORITIES 4 bytes).
Tighten both loops to require
acesize >= offsetof(struct smbace, sid) + CIFSSIDBASESIZE
which is the smallest valid on-wire ACE layout (4-byte header + 4-byte accessreq + 8-byte sid base with zero sub-auths). Also reject ACEs whose sid.numsubauth exceeds SIDMAXSUBAUTHORITIES before letting comparesids() dereference subauth[] entries.
parsesecdesc() already enforces an equivalent check (lines 441-448); smbcheckpermdacl() simply grew weaker validation over time.
Reachability: authenticated SMB client with permission to set an ACL on a file. On a subsequent CREATE against that file, the kernel walks the stored DACL via smbcheckpermdacl() and triggers the OOB read. Not pre-auth, and the OOB read is not reflected to the attacker, but KASAN reports and kernel state corruption are possible.
In the Linux kernel, the following vulnerability has been resolved:
ksmbd: validate numaces and harden ACE walk in smbinheritdacl()
smbinheritdacl() trusts the on-disk numaces value from the parent directory's DACL xattr and uses it to size a heap allocation:
acesbase = kmalloc(sizeof(struct smbace) numaces 2, ...);
numaces is a u16 read from le16tocpu(parentpdacl->numaces) without checking that it is consistent with the declared pdaclsize. An authenticated client whose parent directory's security.NTACL is tampered (e.g. via offline xattr corruption or a concurrent path that bypasses parsedacl()) can present numaces = 65535 with minimal actual ACE data. This causes a ~8 MB allocation (not kzalloc, so uninitialized) that the subsequent loop only partially populates, and may also overflow the three-way sizet multiply on 32-bit kernels.
Additionally, the ACE walk loop uses the weaker offsetof(struct smbace, accessreq) minimum size check rather than the minimum valid on-wire ACE size, and does not reject ACEs whose declared size is below the minimum.
Reproduced on UML + KASAN + LOCKDEP against the real ksmbd code path. A legitimate mount.cifs client creates a parent directory over SMB (ksmbd writes a valid security.NTACL xattr), then the NTACL blob on the backing filesystem is rewritten to set numaces = 0xFFFF while keeping the posixaclhash bytes intact so ksmbdvfsgetsdxattr()'s hash check still passes. A subsequent SMB2 CREATE of a child under that parent drives smb2open() into smbinheritdacl() (share has "vfs objects = aclxattr" set), which fails the page allocator:
WARNING: mm/pagealloc.c:5226 at allocfrozenpagesnoprof+0x46c/0x9c0 Workqueue: ksmbd-io handleksmbdwork allocfrozenpagesnoprof+0x46c/0x9c0 kmalloclargenode+0x68/0x130 kmalloclargenodenoprof+0x24/0x70 kmallocnoprof+0x4c9/0x690 smbinheritdacl+0x394/0x2430 smb2open+0x595d/0xabe0 handleksmbdwork+0x3d3/0x1140
With the patch applied the added guard rejects the tampered value with -EINVAL before any large allocation runs, smb2open() falls back to smb2createsdbuffer(), and the child is created with a default SD. No warning, no splat.
Fix by:
1. Validating numaces against pdaclsize using the same formula applied in parsedacl().
2. Replacing the raw kmalloc(sizeof numaces 2) with kmallocarray(numaces 2, sizeof(...)) for overflow-safe allocation.
3. Tightening the per-ACE loop guard to require the minimum valid ACE size (offsetof(smbace, sid) + CIFSSIDBASESIZE) and rejecting under-sized ACEs, matching the hardening in smbcheckpermdacl() and parsedacl().
v1 -> v2: - Replace the synthetic test-module splat in the changelog with a real-path UML + KASAN reproduction driven through mount.cifs and SMB2 CREATE; Namjae flagged the kcifs3testinheritdaclold name in v1 since it does not exist in ksmbd. - Drop the commit-hash citation from the code comment per Namjae's review; keep the parsedacl() pointer.
In the Linux kernel, the following vulnerability has been resolved:
ksmbd: require 3 sub-authorities before reading subauth[2]
parsedacl() compares each ACE SID against sidunixNFSmode and on match reads sid.subauth[2] as the file mode. If sidunixNFSmode is the prefix S-1-5-88-3 with numsubauth = 2 then comparesids() compares only min(numsubauth, 2) sub-authorities so a client SID with numsubauth = 2 and subauth = {88, 3} will match.
If numsubauth = 2 and the ACE is placed at the very end of the security descriptor, subauth[2] will be 4 bytes past endofacl. The out-of-band bytes will then be masked to the low 9 bits and applied as the file's POSIX mode, probably not something that is good to have happen.
Fix this up by forcing the SID to actually carry a third sub-authority before reading it at all.
In the Linux kernel, the following vulnerability has been resolved:
ksmbd: do not expire session on binding failure
When a multichannel session binding request fails (e.g. wrong password), the error path unconditionally sets sess->state = SMB2SESSIONEXPIRED. However, during binding, sess points to the target session looked up via ksmbdsessionlookupslowpath() -- which belongs to another connection's user. This allows a remote attacker to invalidate any active session by simply sending a binding request with a wrong password (DoS).
Fix this by skipping session expiration when the failed request was a binding attempt, since the session does not belong to the current connection. The reference taken by ksmbdsessionlookupslowpath() is still correctly released via ksmbdusersessionput().