]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ksmbd: fix overflow in dacloffset bounds check
authorNorbert Szetei <norbert@doyensec.com>
Sat, 29 Mar 2025 06:58:15 +0000 (06:58 +0000)
committerSteve French <stfrench@microsoft.com>
Tue, 1 Apr 2025 05:04:21 +0000 (00:04 -0500)
The dacloffset field was originally typed as int and used in an
unchecked addition, which could overflow and bypass the existing
bounds check in both smb_check_perm_dacl() and smb_inherit_dacl().

This could result in out-of-bounds memory access and a kernel crash
when dereferencing the DACL pointer.

This patch converts dacloffset to unsigned int and uses
check_add_overflow() to validate access to the DACL.

Cc: stable@vger.kernel.org
Signed-off-by: Norbert Szetei <norbert@doyensec.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/smbacl.c

index 49b128698670a826beb01d65ee6e53c38ca5a407..2693ea2d67aa7deb7b965124fe2fa7a2c54cf561 100644 (file)
@@ -1026,7 +1026,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
        struct dentry *parent = path->dentry->d_parent;
        struct mnt_idmap *idmap = mnt_idmap(path->mnt);
        int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
-       int rc = 0, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
+       int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size;
+       unsigned int dacloffset;
+       size_t dacl_struct_end;
        u16 num_aces, ace_cnt = 0;
        char *aces_base;
        bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
@@ -1035,8 +1037,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
                                            parent, &parent_pntsd);
        if (pntsd_size <= 0)
                return -ENOENT;
+
        dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
-       if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
+       if (!dacloffset ||
+           check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) ||
+           dacl_struct_end > (size_t)pntsd_size) {
                rc = -EINVAL;
                goto free_parent_pntsd;
        }
@@ -1240,7 +1245,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
        struct smb_ntsd *pntsd = NULL;
        struct smb_acl *pdacl;
        struct posix_acl *posix_acls;
-       int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
+       int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size;
+       unsigned int dacl_offset;
+       size_t dacl_struct_end;
        struct smb_sid sid;
        int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
        struct smb_ace *ace;
@@ -1259,7 +1266,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
 
        dacl_offset = le32_to_cpu(pntsd->dacloffset);
        if (!dacl_offset ||
-           (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
+           check_add_overflow(dacl_offset, sizeof(struct smb_acl), &dacl_struct_end) ||
+           dacl_struct_end > (size_t)pntsd_size)
                goto err_out;
 
        pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));