Re: [PATCH] smb: client: validate dacloffset before building DACL pointers
Steve French <[email protected]> Sun, 3 May 2026 10:24:11 -0500
| Newsgroups | gmane.linux.kernel.stable,gmane.linux.kernel.cifs,gmane.network.samba.internals,gmane.linux.kernel |
|---|---|
| Message-ID | <CAH2r5mv5wV6xJfmTNx3dTbs1wTDyYpLPsdq53butKq1N_OvAoQ@mail.gmail.com> |
merged into cifs-2.6.git for-next pending additional testing and review On Mon, Apr 20, 2026 at 10:44 AM Michael Bommarito <[email protected]> wrote: > > 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. > > Fixes: bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.") > Cc: [email protected] > Assisted-by: Claude:claude-opus-4-6 > Signed-off-by: Michael Bommarito <[email protected]> > --- > This applies on top of > > [PATCH v2] smb: client: validate the whole DACL before rewriting it > in cifsacl > https://lore.kernel.org/linux-cifs/[email protected]/ > > so that the new dacl_offset_valid() numeric precheck sits upstream of > that series' validate_dacl() structural check at all three call sites. > The two patches are independent fixes for different bug classes on the > same three entry points; applying this one without the KCIFS2 v2 patch > first will fail on the build_sec_desc() hunk because the trailing > context line "rc = validate_dacl(dacl_ptr, end_of_acl)" only exists > after v2. If you prefer a different ordering, happy to reroll on a > plain mainline base instead. > > fs/smb/client/cifsacl.c | 35 ++++++++++++++++++++++++++++++++--- > 1 file changed, 32 insertions(+), 3 deletions(-) > > diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c > index cb4060ba5e31..87d2a58fc8b4 100644 > --- a/fs/smb/client/cifsacl.c > +++ b/fs/smb/client/cifsacl.c > @@ -1263,6 +1263,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, > @@ -1283,7 +1294,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), > @@ -1314,11 +1324,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; > } > @@ -1341,6 +1358,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) > @@ -1709,6 +1731,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) { > @@ -1751,6 +1779,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); > -- > 2.53.0 > > -- Thanks, Steve