FAILED: patch "[PATCH] ksmbd: require minimum ACE size in smb_check_perm_dacl()" failed to apply to 6.1-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x d07b26f39246a82399661936dd0c853983cfade7
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026042427-lunchtime-paramount-5994@gregkh' --subject-prefix 'PATCH 6.1.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From d07b26f39246a82399661936dd0c853983cfade7 Mon Sep 17 00:00:00 2001
From: Michael Bommarito <[email protected]>
Date: Tue, 14 Apr 2026 15:15:33 -0400
Subject: [PATCH] ksmbd: require minimum ACE size in smb_check_perm_dacl()
Both ACE-walk loops in smb_check_perm_dacl() 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 smb_ace, access_req) > aces_size)
break;
ace_size = le16_to_cpu(ace->size);
if (ace_size > aces_size)
break;The first check only requires the 4-byte ACE header to be in bounds; it does not require access_req (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 aces_size == 4, pass both checks, and then
granted |= le32_to_cpu(ace->access_req); /* upper loop */
compare_sids(&sid, &ace->sid); /* lower loop */
reads access_req at offset 4 (OOB by up to 4 bytes) and ace->sid at offset 8 (OOB by up to CIFS_SID_BASE_SIZE + SID_MAX_SUB_AUTHORITIES
* 4 bytes).
Tighten both loops to require
ace_size >= offsetof(struct smb_ace, sid) + CIFS_SID_BASE_SIZE
which is the smallest valid on-wire ACE layout (4-byte header + 4-byte access_req + 8-byte sid base with zero sub-auths). Also reject ACEs whose sid.num_subauth exceeds SID_MAX_SUB_AUTHORITIES before letting compare_sids() dereference sub_auth[] entries.
parse_sec_desc() already enforces an equivalent check (lines 441-448); smb_check_perm_dacl() 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 smb_check_perm_dacl() 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.
fs/smb/server/smbacl.c +13 -4
@@ -1342,10 +1342,13 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl +sizeof (struct smb_acl )); aces_size = acl_size -sizeof (struct smb_acl );for (i =0 ; i <le16_to_cpu (pdacl->num_aces); i++) { -if (offsetof(struct smb_ace , access_req) > aces_size) +if (offsetof(struct smb_ace , sid) + + aces_size <CIFS_SID_BASE_SIZE )break ; ace_size =le16_to_cpu (ace->size); -if (ace_size > aces_size) +if (ace_size > aces_size || + ace_size < offsetof(struct smb_ace , sid) + +CIFS_SID_BASE_SIZE )break ; aces_size -= ace_size; granted |=le32_to_cpu (ace->access_req);
@@ -1360,13 +1363,19 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl +sizeof (struct smb_acl )); aces_size = acl_size -sizeof (struct smb_acl );for (i =0 ; i <le16_to_cpu (pdacl->num_aces); i++) { -if (offsetof(struct smb_ace , access_req) > aces_size) +if (offsetof(struct smb_ace , sid) + + aces_size <CIFS_SID_BASE_SIZE )break ; ace_size =le16_to_cpu (ace->size); -if (ace_size > aces_size) +if (ace_size > aces_size || + ace_size < offsetof(struct smb_ace , sid) + +CIFS_SID_BASE_SIZE )break ; aces_size -= ace_size; +if (ace->sid.num_subauth >SID_MAX_SUB_AUTHORITIES ) +break ; +if (!compare_sids (&sid, &ace->sid) || !compare_sids (&sid_unix_NFS_mode, &ace->sid)) { found =1 ;
Sasha Levin
maintainerPATCH 1/1 ksmbd: require minimum ACE size in smb_check_perm_dacl()
From: Michael Bommarito <[email protected]>
[ Upstream commit d07b26f39246a82399661936dd0c853983cfade7 ]
Both ACE-walk loops in smb_check_perm_dacl() 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 smb_ace, access_req) > aces_size)
break;
ace_size = le16_to_cpu(ace->size);
if (ace_size > aces_size)
break;The first check only requires the 4-byte ACE header to be in bounds; it does not require access_req (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 aces_size == 4, pass both checks, and then
granted |= le32_to_cpu(ace->access_req); /* upper loop */
compare_sids(&sid, &ace->sid); /* lower loop */
reads access_req at offset 4 (OOB by up to 4 bytes) and ace->sid at offset 8 (OOB by up to CIFS_SID_BASE_SIZE + SID_MAX_SUB_AUTHORITIES
* 4 bytes).
Tighten both loops to require
ace_size >= offsetof(struct smb_ace, sid) + CIFS_SID_BASE_SIZE
which is the smallest valid on-wire ACE layout (4-byte header + 4-byte access_req + 8-byte sid base with zero sub-auths). Also reject ACEs whose sid.num_subauth exceeds SID_MAX_SUB_AUTHORITIES before letting compare_sids() dereference sub_auth[] entries.
parse_sec_desc() already enforces an equivalent check (lines 441-448); smb_check_perm_dacl() 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 smb_check_perm_dacl() 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.
--- fs/smb/server/smbacl.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-)
fs/smb/server/smbacl.c +13 -4
@@ -1288,10 +1288,13 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl +sizeof (struct smb_acl )); aces_size = acl_size -sizeof (struct smb_acl );for (i =0 ; i <le32_to_cpu (pdacl->num_aces); i++) { -if (offsetof(struct smb_ace , access_req) > aces_size) +if (offsetof(struct smb_ace , sid) + + aces_size <CIFS_SID_BASE_SIZE )break ; ace_size =le16_to_cpu (ace->size); -if (ace_size > aces_size) +if (ace_size > aces_size || + ace_size < offsetof(struct smb_ace , sid) + +CIFS_SID_BASE_SIZE )break ; aces_size -= ace_size; granted |=le32_to_cpu (ace->access_req);
@@ -1309,13 +1312,19 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl +sizeof (struct smb_acl )); aces_size = acl_size -sizeof (struct smb_acl );for (i =0 ; i <le32_to_cpu (pdacl->num_aces); i++) { -if (offsetof(struct smb_ace , access_req) > aces_size) +if (offsetof(struct smb_ace , sid) + + aces_size <CIFS_SID_BASE_SIZE )break ; ace_size =le16_to_cpu (ace->size); -if (ace_size > aces_size) +if (ace_size > aces_size || + ace_size < offsetof(struct smb_ace , sid) + +CIFS_SID_BASE_SIZE )break ; aces_size -= ace_size; +if (ace->sid.num_subauth >SID_MAX_SUB_AUTHORITIES ) +break ; +if (!compare_sids (&sid, &ace->sid) || !compare_sids (&sid_unix_NFS_mode, &ace->sid)) { found =1 ;
Signature · 1 line
2.53.0