FAILED: patch "[PATCH] smb: client: validate dacloffset before building DACL" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-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-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x f98b48151cc502ada59d9778f0112d21f2586ca3
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026051249-smartness-undercoat-cf5c@gregkh' --subject-prefix 'PATCH 5.15.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From f98b48151cc502ada59d9778f0112d21f2586ca3 Mon Sep 17 00:00:00 2001
From: Michael Bommarito <[email protected]>
Date: Mon, 20 Apr 2026 10:47:47 -0400
Subject: [PATCH] smb: client: validate dacloffset before building DACL
pointers
parse_sec_desc(), build_sec_desc(), and the chown path in id_mode_to_cifs_acl() all add the server-supplied dacloffset to pntsd before proving a DACL header fits inside the returned security descriptor.
On 32-bit builds a malicious server can return dacloffset near U32_MAX, wrap the derived DACL pointer below end_of_acl, and then slip past the later pointer-based bounds checks. build_sec_desc() and id_mode_to_cifs_acl() can then dereference DACL fields from the wrapped pointer in the chmod/chown rewrite paths.
Validate dacloffset numerically before building any DACL pointer and reuse the same helper at the three DACL entry points.
fs/smb/client/cifsacl.c +32 -3
@@ -1264,6 +1264,17 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl)return 0 ; } +static bool dacl_offset_valid (unsigned int acl_len,__u32 dacloffset) +{ +if (acl_len <sizeof (struct smb_acl )) +return false; + +if (dacloffset <sizeof (struct smb_ntsd )) +return false; + +return dacloffset <= acl_len -sizeof (struct smb_acl ); +} +/* Convert CIFS ACL to POSIX form */ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
@@ -1284,7 +1295,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, group_sid_ptr = (struct smb_sid *)((char *)pntsd +le32_to_cpu (pntsd->gsidoffset)); dacloffset =le32_to_cpu (pntsd->dacloffset); - dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), le32_to_cpu(pntsd->gsidoffset),
@@ -1315,11 +1325,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,return rc; } -if (dacloffset) +if (dacloffset) { +if (!dacl_offset_valid (acl_len, dacloffset)) { +cifs_dbg (VFS ,"Server returned illegal DACL offset\n" ); +return -EINVAL ; + } + + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);parse_dacl (dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, fattr, get_mode_from_special_sid); -else + }else {cifs_dbg (FYI ,"no ACL\n" );/* BB grant all or default perms? */ + }return rc; }
@@ -1342,6 +1359,11 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, dacloffset =le32_to_cpu (pntsd->dacloffset);if (dacloffset) { +if (!dacl_offset_valid (secdesclen, dacloffset)) { +cifs_dbg (VFS ,"Server returned illegal DACL offset\n" ); +return -EINVAL ; + } + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); rc =validate_dacl (dacl_ptr, end_of_acl);if (rc)
@@ -1710,6 +1732,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, nsecdesclen =sizeof (struct smb_ntsd ) + (sizeof (struct smb_sid ) *2 ); dacloffset =le32_to_cpu (pntsd->dacloffset);if (dacloffset) { +if (!dacl_offset_valid (secdesclen, dacloffset)) { +cifs_dbg (VFS ,"Server returned illegal DACL offset\n" ); + rc = -EINVAL ; + goto id_mode_to_cifs_acl_exit; + } + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); rc =validate_dacl (dacl_ptr, (char *)pntsd + secdesclen);if (rc) {
@@ -1752,6 +1780,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, rc = ops->set_acl (pnntsd, nsecdesclen, inode, path, aclflag);cifs_dbg (NOISY ,"set_cifs_acl rc: %d\n" , rc); } +id_mode_to_cifs_acl_exit:cifs_put_tlink (tlink);kfree (pnntsd);
Sasha Levin
maintainerPATCH 1/1 smb: client: validate dacloffset before building DACL pointers
From: Michael Bommarito <[email protected]>
[ Upstream commit f98b48151cc502ada59d9778f0112d21f2586ca3 ]
parse_sec_desc(), build_sec_desc(), and the chown path in id_mode_to_cifs_acl() all add the server-supplied dacloffset to pntsd before proving a DACL header fits inside the returned security descriptor.
On 32-bit builds a malicious server can return dacloffset near U32_MAX, wrap the derived DACL pointer below end_of_acl, and then slip past the later pointer-based bounds checks. build_sec_desc() and id_mode_to_cifs_acl() can then dereference DACL fields from the wrapped pointer in the chmod/chown rewrite paths.
Validate dacloffset numerically before building any DACL pointer and reuse the same helper at the three DACL entry points.
--- fs/cifs/cifsacl.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-)
fs/cifs/cifsacl.c +32 -3
@@ -1183,6 +1183,17 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)return 0 ; } +static bool dacl_offset_valid (unsigned int acl_len,__u32 dacloffset) +{ +if (acl_len <sizeof (struct cifs_acl )) +return false; + +if (dacloffset <sizeof (struct cifs_ntsd )) +return false; + +return dacloffset <= acl_len -sizeof (struct cifs_acl ); +} +/* Convert CIFS ACL to POSIX form */ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
@@ -1203,7 +1214,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, group_sid_ptr = (struct cifs_sid *)((char *)pntsd +le32_to_cpu (pntsd->gsidoffset)); dacloffset =le32_to_cpu (pntsd->dacloffset); - dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n", pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), le32_to_cpu(pntsd->gsidoffset),
@@ -1234,11 +1244,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,return rc; } -if (dacloffset) +if (dacloffset) { +if (!dacl_offset_valid (acl_len, dacloffset)) { +cifs_dbg (VFS ,"Server returned illegal DACL offset\n" ); +return -EINVAL ; + } + + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);parse_dacl (dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, fattr, get_mode_from_special_sid); -else + }else {cifs_dbg (FYI ,"no ACL\n" );/* BB grant all or default perms? */ + }return rc; }
@@ -1261,6 +1278,11 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, dacloffset =le32_to_cpu (pntsd->dacloffset);if (dacloffset) { +if (!dacl_offset_valid (secdesclen, dacloffset)) { +cifs_dbg (VFS ,"Server returned illegal DACL offset\n" ); +return -EINVAL ; + } + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);if (end_of_acl < (char *)dacl_ptr +le16_to_cpu (dacl_ptr->size)) {cifs_dbg (VFS ,"Server returned illegal ACL size\n" );
@@ -1624,6 +1646,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, nsecdesclen =sizeof (struct cifs_ntsd ) + (sizeof (struct cifs_sid ) *2 ); dacloffset =le32_to_cpu (pntsd->dacloffset);if (dacloffset) { +if (!dacl_offset_valid (secdesclen, dacloffset)) { +cifs_dbg (VFS ,"Server returned illegal DACL offset\n" ); + rc = -EINVAL ; + goto id_mode_to_cifs_acl_exit; + } + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);if (mode_from_sid) nsecdesclen +=
@@ -1660,6 +1688,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, rc = ops->set_acl (pnntsd, nsecdesclen, inode, path, aclflag);cifs_dbg (NOISY ,"set_cifs_acl rc: %d\n" , rc); } +id_mode_to_cifs_acl_exit:cifs_put_tlink (tlink);kfree (pnntsd);
Signature · 1 line
2.53.0