VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46195

CVE-2026-46195

Description

In the Linux kernel, the following vulnerability has been resolved:

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.

Affected products

2

Patches

10
3b1ddba19e77

smb: client: validate dacloffset before building DACL pointers

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 20, 2026Fixed in 6.12.88via kernel-cna
1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index 1026cbcf1d4fcd..2422ac371262b3 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -1265,6 +1265,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,
    @@ -1285,7 +1296,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),
    @@ -1316,11 +1326,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;
     }
    @@ -1343,6 +1360,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)
    @@ -1716,6 +1738,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) {
    @@ -1758,6 +1786,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);
    -- 
    cgit 1.3-korg
    
    
    
c688f3ed73d3

smb: client: validate dacloffset before building DACL pointers

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 20, 2026Fixed in 6.18.30via kernel-cna
1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index 5b43d5ddfd94ad..e2948835587094 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -1215,6 +1215,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,
    @@ -1235,7 +1246,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),
    @@ -1266,11 +1276,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;
     }
    @@ -1293,6 +1310,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);
     		if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
     			cifs_dbg(VFS, "Server returned illegal ACL size\n");
    @@ -1669,6 +1691,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);
     			if (mode_from_sid)
     				nsecdesclen +=
    @@ -1705,6 +1733,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);
    -- 
    cgit 1.3-korg
    
    
    
8bd07e417b6b

smb: client: validate dacloffset before building DACL pointers

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 20, 2026Fixed in 7.0.7via kernel-cna
1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index 62fccba6fede72..e5d0d581c130bf 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -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);
    -- 
    cgit 1.3-korg
    
    
    
f98b48151cc5

smb: client: validate dacloffset before building DACL pointers

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 20, 2026Fixed in 7.1-rc3via kernel-cna
1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index a2750f1e3d90bd..786dbbc43c5b99 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -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);
    -- 
    cgit 1.3-korg
    
    
    
ba7f71b6161c

smb: client: validate dacloffset before building DACL pointers

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 20, 2026Fixed in 6.6.140via kernel-cna
1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index fb090e001e444e..48d78a41b8afe1 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -1216,6 +1216,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,
    @@ -1236,7 +1247,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),
    @@ -1267,11 +1277,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;
     }
    @@ -1294,6 +1311,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);
     		if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
     			cifs_dbg(VFS, "Server returned illegal ACL size\n");
    @@ -1668,6 +1690,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);
     			if (mode_from_sid)
     				nsecdesclen +=
    @@ -1704,6 +1732,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);
    -- 
    cgit 1.3-korg
    
    
    
f98b48151cc5

smb: client: validate dacloffset before building DACL pointers

1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index a2750f1e3d90bd..786dbbc43c5b99 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -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);
    -- 
    cgit 1.3-korg
    
    
    
8bd07e417b6b

smb: client: validate dacloffset before building DACL pointers

1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index 62fccba6fede72..e5d0d581c130bf 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -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);
    -- 
    cgit 1.3-korg
    
    
    
ba7f71b6161c

smb: client: validate dacloffset before building DACL pointers

1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index fb090e001e444e..48d78a41b8afe1 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -1216,6 +1216,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,
    @@ -1236,7 +1247,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),
    @@ -1267,11 +1277,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;
     }
    @@ -1294,6 +1311,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);
     		if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
     			cifs_dbg(VFS, "Server returned illegal ACL size\n");
    @@ -1668,6 +1690,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);
     			if (mode_from_sid)
     				nsecdesclen +=
    @@ -1704,6 +1732,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);
    -- 
    cgit 1.3-korg
    
    
    
c688f3ed73d3

smb: client: validate dacloffset before building DACL pointers

1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index 5b43d5ddfd94ad..e2948835587094 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -1215,6 +1215,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,
    @@ -1235,7 +1246,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),
    @@ -1266,11 +1276,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;
     }
    @@ -1293,6 +1310,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);
     		if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
     			cifs_dbg(VFS, "Server returned illegal ACL size\n");
    @@ -1669,6 +1691,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);
     			if (mode_from_sid)
     				nsecdesclen +=
    @@ -1705,6 +1733,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);
    -- 
    cgit 1.3-korg
    
    
    
3b1ddba19e77

smb: client: validate dacloffset before building DACL pointers

1 file changed · +32 4
  • fs/smb/client/cifsacl.c+32 4 modified
    diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
    index 1026cbcf1d4fcd..2422ac371262b3 100644
    --- a/fs/smb/client/cifsacl.c
    +++ b/fs/smb/client/cifsacl.c
    @@ -1265,6 +1265,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,
    @@ -1285,7 +1296,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),
    @@ -1316,11 +1326,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;
     }
    @@ -1343,6 +1360,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)
    @@ -1716,6 +1738,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) {
    @@ -1758,6 +1786,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);
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing numerical validation of the server-supplied dacloffset before pointer arithmetic, allowing integer wrap on 32-bit builds to bypass later pointer-based bounds checks."

Attack vector

A malicious SMB server sends a crafted security descriptor with a dacloffset value near U32_MAX. On 32-bit client builds, adding this offset to the base pointer wraps the resulting DACL pointer below end_of_acl, causing the subsequent pointer-based bounds checks to pass. The attacker can then trigger the chmod/chown rewrite paths in build_sec_desc() or id_mode_to_cifs_acl(), which dereference DACL fields from the wrapped pointer, leading to out-of-bounds read/write [patch_id=2897865]. No authentication beyond normal SMB session establishment is required; the server simply returns the malicious descriptor in any SMB2/3 query operation that includes a security descriptor.

Affected code

The vulnerability exists in fs/smb/client/cifsacl.c in three functions: parse_sec_desc(), build_sec_desc(), and id_mode_to_cifs_acl(). All three compute a DACL pointer by adding the server-supplied dacloffset to pntsd without first validating the offset numerically [patch_id=2897865].

What the fix does

The patch introduces a new helper function dacl_offset_valid() that performs a numerical bounds check on dacloffset before any pointer is constructed [patch_id=2897865]. It verifies that the ACL length is at least sizeof(struct smb_acl), that dacloffset is at least sizeof(struct smb_ntsd), and that dacloffset does not exceed acl_len - sizeof(struct smb_acl). This check is inserted at all three DACL entry points — parse_sec_desc(), build_sec_desc(), and id_mode_to_cifs_acl() — before the pointer (char *)pntsd + dacloffset is computed. If the offset is invalid, the function returns -EINVAL and logs a warning, preventing any DACL pointer from being built from a malicious offset.

Preconditions

  • configClient must be running a 32-bit kernel build where pointer arithmetic can wrap
  • networkAttacker must control a malicious SMB server that the client connects to
  • inputClient must perform an operation that parses a security descriptor (e.g., stat, chmod, chown on a CIFS/SMB mount)

Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.