CVE-2022-47942: Out-of-bounds Write (CWE-787)
value: An issue was discovered in ksmbd in the Linux kernel 5.15 through 5.19 before 5.19.2. There is a heap-based buffer overflow in set_ntacl_dacl, related to use of SMB2_QUERY_INFO_HE after a malformed SMB2_SET_INFO_HE command.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 8.8
Published: 2022-12-23T16:15Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: fs/ksmbd/smb2pdu.c, fs/ksmbd/smbacl.c, fs/ksmbd/smbacl.h, fs/ksmbd/vfs.c
Commit date: 2022-08-04 09:51:38 -0500
Commit message: ksmbd: fix heap-based overflow in set_ntacl_dacl()

The testcase use SMB2_SET_INFO_HE command to set a malformed file attribute
under the label `security.NTACL`. SMB2_QUERY_INFO_HE command in testcase
trigger the following overflow.

[ 4712.003781] ==================================================================
[ 4712.003790] BUG: KASAN: slab-out-of-bounds in build_sec_desc+0x842/0x1dd0 [ksmbd]
[ 4712.003807] Write of size 1060 at addr ffff88801e34c068 by task kworker/0:0/4190

[ 4712.003813] CPU: 0 PID: 4190 Comm: kworker/0:0 Not tainted 5.19.0-rc5 #1
[ 4712.003850] Workqueue: ksmbd-io handle_ksmbd_work [ksmbd]
[ 4712.003867] Call Trace:
[ 4712.003870]  <TASK>
[ 4712.003873]  dump_stack_lvl+0x49/0x5f
[ 4712.003935]  print_report.cold+0x5e/0x5cf
[ 4712.003972]  ? ksmbd_vfs_get_sd_xattr+0x16d/0x500 [ksmbd]
[ 4712.003984]  ? cmp_map_id+0x200/0x200
[ 4712.003988]  ? build_sec_desc+0x842/0x1dd0 [ksmbd]
[ 4712.004000]  kasan_report+0xaa/0x120
[ 4712.004045]  ? build_sec_desc+0x842/0x1dd0 [ksmbd]
[ 4712.004056]  kasan_check_range+0x100/0x1e0
[ 4712.004060]  memcpy+0x3c/0x60
[ 4712.004064]  build_sec_desc+0x842/0x1dd0 [ksmbd]
[ 4712.004076]  ? parse_sec_desc+0x580/0x580 [ksmbd]
[ 4712.004088]  ? ksmbd_acls_fattr+0x281/0x410 [ksmbd]
[ 4712.004099]  smb2_query_info+0xa8f/0x6110 [ksmbd]
[ 4712.004111]  ? psi_group_change+0x856/0xd70
[ 4712.004148]  ? update_load_avg+0x1c3/0x1af0
[ 4712.004152]  ? asym_cpu_capacity_scan+0x5d0/0x5d0
[ 4712.004157]  ? xas_load+0x23/0x300
[ 4712.004162]  ? smb2_query_dir+0x1530/0x1530 [ksmbd]
[ 4712.004173]  ? _raw_spin_lock_bh+0xe0/0xe0
[ 4712.004179]  handle_ksmbd_work+0x30e/0x1020 [ksmbd]
[ 4712.004192]  process_one_work+0x778/0x11c0
[ 4712.004227]  ? _raw_spin_lock_irq+0x8e/0xe0
[ 4712.004231]  worker_thread+0x544/0x1180
[ 4712.004234]  ? __cpuidle_text_end+0x4/0x4
[ 4712.004239]  kthread+0x282/0x320
[ 4712.004243]  ? process_one_work+0x11c0/0x11c0
[ 4712.004246]  ? kthread_complete_and_exit+0x30/0x30
[ 4712.004282]  ret_from_fork+0x1f/0x30

This patch add the buffer validation for security descriptor that is
stored by malformed SMB2_SET_INFO_HE command. and allocate large
response buffer about SMB2_O_INFO_SECURITY file info class.

Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Cc: stable@vger.kernel.org
Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-17771
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>

Diff: diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c
index 5943cb17a9b0..9751cc92c111 100644
--- a/fs/ksmbd/smb2pdu.c
+++ b/fs/ksmbd/smb2pdu.c
@@ -533,13 +533,14 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
 
 	if (cmd == SMB2_QUERY_INFO_HE) {
 		struct smb2_query_info_req *req;
 
 		req = smb2_get_msg(work->request_buf);
-		if (req->InfoType == SMB2_O_INFO_FILE &&
-		    (req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
-		     req->FileInfoClass == FILE_ALL_INFORMATION))
+		if ((req->InfoType == SMB2_O_INFO_FILE &&
+		     (req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
+		     req->FileInfoClass == FILE_ALL_INFORMATION)) ||
+		    req->InfoType == SMB2_O_INFO_SECURITY)
 			sz = large_sz;
 	}
 
 	/* allocate large response buf for chained commands */
 	if (le32_to_cpu(hdr->NextCommand) > 0)
@@ -2986,11 +2987,11 @@ int smb2_open(struct ksmbd_work *work)
 							GFP_KERNEL);
 					if (!pntsd)
 						goto err_out;
 
 					rc = build_sec_desc(user_ns,
-							    pntsd, NULL,
+							    pntsd, NULL, 0,
 							    OWNER_SECINFO |
 							    GROUP_SECINFO |
 							    DACL_SECINFO,
 							    &pntsd_size, &fattr);
 					posix_acl_release(fattr.cf_acls);
@@ -3831,22 +3832,29 @@ static int verify_info_level(int info_level)
 	}
 
 	return 0;
 }
 
+static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len)
+{
+	int free_len;
+
+	free_len = (int)(work->response_sz -
+		(get_rfc1002_len(work->response_buf) + 4)) - hdr2_len;
+	return free_len;
+}
+
 static int smb2_calc_max_out_buf_len(struct ksmbd_work *work,
 				     unsigned short hdr2_len,
 				     unsigned int out_buf_len)
 {
 	int free_len;
 
 	if (out_buf_len > work->conn->vals->max_trans_size)
 		return -EINVAL;
 
-	free_len = (int)(work->response_sz -
-			 (get_rfc1002_len(work->response_buf) + 4)) -
-		hdr2_len;
+	free_len = smb2_resp_buf_len(work, hdr2_len);
 	if (free_len < 0)
 		return -EINVAL;
 
 	return min_t(int, out_buf_len, free_len);
 }
@@ -5105,14 +5113,14 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
 	struct ksmbd_file *fp;
 	struct user_namespace *user_ns;
 	struct smb_ntsd *pntsd = (struct smb_ntsd *)rsp->Buffer, *ppntsd = NULL;
 	struct smb_fattr fattr = {{0}};
 	struct inode *inode;
-	__u32 secdesclen;
+	__u32 secdesclen = 0;
 	unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID;
 	int addition_info = le32_to_cpu(req->AdditionalInformation);
-	int rc;
+	int rc = 0, ppntsd_size = 0;
 
 	if (addition_info & ~(OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
 			      PROTECTED_DACL_SECINFO |
 			      UNPROTECTED_DACL_SECINFO)) {
 		ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n",
@@ -5154,15 +5162,18 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
 	inode = file_inode(fp->filp);
 	ksmbd_acls_fattr(&fattr, user_ns, inode);
 
 	if (test_share_config_flag(work->tcon->share_conf,
 				   KSMBD_SHARE_FLAG_ACL_XATTR))
-		ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
-				       fp->filp->f_path.dentry, &ppntsd);
-
-	rc = build_sec_desc(user_ns, pntsd, ppntsd, addition_info,
-			    &secdesclen, &fattr);
+		ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
+						     fp->filp->f_path.dentry,
+						     &ppntsd);
+
+	/* Check if sd buffer size exceeds response buffer size */
+	if (smb2_resp_buf_len(work, 8) > ppntsd_size)
+		rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size,
+				    addition_info, &secdesclen, &fattr);
 	posix_acl_release(fattr.cf_acls);
 	posix_acl_release(fattr.cf_dacls);
 	kfree(ppntsd);
 	ksmbd_fd_put(work, fp);
 	if (rc)
diff --git a/fs/ksmbd/smbacl.c b/fs/ksmbd/smbacl.c
index 38f23bf981ac..3781bca2c8fc 100644
--- a/fs/ksmbd/smbacl.c
+++ b/fs/ksmbd/smbacl.c
@@ -688,10 +688,11 @@ static void set_posix_acl_entries_dacl(struct user_namespace *user_ns,
 }
 
 static void set_ntacl_dacl(struct user_namespace *user_ns,
 			   struct smb_acl *pndacl,
 			   struct smb_acl *nt_dacl,
+			   unsigned int aces_size,
 			   const struct smb_sid *pownersid,
 			   const struct smb_sid *pgrpsid,
 			   struct smb_fattr *fattr)
 {
 	struct smb_ace *ntace, *pndace;
@@ -701,13 +702,23 @@ static void set_ntacl_dacl(struct user_namespace *user_ns,
 
 	pndace = (struct smb_ace *)((char *)pndacl + sizeof(struct smb_acl));
 	if (nt_num_aces) {
 		ntace = (struct smb_ace *)((char *)nt_dacl + sizeof(struct smb_acl));
 		for (i = 0; i < nt_num_aces; i++) {
-			memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size));
-			size += le16_to_cpu(ntace->size);
-			ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size));
+			unsigned short nt_ace_size;
+
+			if (offsetof(struct smb_ace, access_req) > aces_size)
+				break;
+
+			nt_ace_size = le16_to_cpu(ntace->size);
+			if (nt_ace_size > aces_size)
+				break;
+
+			memcpy((char *)pndace + size, ntace, nt_ace_size);
+			size += nt_ace_size;
+			aces_size -= nt_ace_size;
+			ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
 			num_aces++;
 		}
 	}
 
 	set_posix_acl_entries_dacl(user_ns, pndace, fattr,
@@ -876,11 +887,11 @@ int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
 }
 
 /* Convert permission bits from mode to equivalent CIFS ACL */
 int build_sec_desc(struct user_namespace *user_ns,
 		   struct smb_ntsd *pntsd, struct smb_ntsd *ppntsd,
-		   int addition_info, __u32 *secdesclen,
+		   int ppntsd_size, int addition_info, __u32 *secdesclen,
 		   struct smb_fattr *fattr)
 {
 	int rc = 0;
 	__u32 offset;
 	struct smb_sid *owner_sid_ptr, *group_sid_ptr;
@@ -936,19 +947,29 @@ int build_sec_desc(struct user_namespace *user_ns,
 		dacl_ptr->size = cpu_to_le16(sizeof(struct smb_acl));
 		dacl_ptr->num_aces = 0;
 
 		if (!ppntsd) {
 			set_mode_dacl(user_ns, dacl_ptr, fattr);
-		} else if (!ppntsd->dacloffset) {
-			goto out;
 		} else {
 			struct smb_acl *ppdacl_ptr;
+			unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset);
+			int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset;
+
+			if (!dacl_offset ||
+			    (dacl_offset + sizeof(struct smb_acl) > ppntsd_size))
+				goto out;
+
+			ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset);
+			ppdacl_size = le16_to_cpu(ppdacl_ptr->size);
+			if (ppdacl_size > ntacl_size ||
+			    ppdacl_size < sizeof(struct smb_acl))
+				goto out;
 
-			ppdacl_ptr = (struct smb_acl *)((char *)ppntsd +
-						le32_to_cpu(ppntsd->dacloffset));
 			set_ntacl_dacl(user_ns, dacl_ptr, ppdacl_ptr,
-				       nowner_sid_ptr, ngroup_sid_ptr, fattr);
+				       ntacl_size - sizeof(struct smb_acl),
+				       nowner_sid_ptr, ngroup_sid_ptr,
+				       fattr);
 		}
 		pntsd->dacloffset = cpu_to_le32(offset);
 		offset += le16_to_cpu(dacl_ptr->size);
 	}
 
@@ -978,43 +999,62 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
 	struct smb_acl *parent_pdacl;
 	struct smb_ntsd *parent_pntsd = NULL;
 	struct smb_sid owner_sid, group_sid;
 	struct dentry *parent = path->dentry->d_parent;
 	struct user_namespace *user_ns = mnt_user_ns(path->mnt);
-	int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0;
-	int rc = 0, num_aces, dacloffset, pntsd_type, acl_len;
+	int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size;
+	int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
 	char *aces_base;
 	bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
 
-	acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns,
-					 parent, &parent_pntsd);
-	if (acl_len <= 0)
+	pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
+					    parent, &parent_pntsd);
+	if (pntsd_size <= 0)
 		return -ENOENT;
 	dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
-	if (!dacloffset) {
+	if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
 		rc = -EINVAL;
 		goto free_parent_pntsd;
 	}
 
 	parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset);
+	acl_len = pntsd_size - dacloffset;
 	num_aces = le32_to_cpu(parent_pdacl->num_aces);
 	pntsd_type = le16_to_cpu(parent_pntsd->type);
+	pdacl_size = le16_to_cpu(parent_pdacl->size);
+
+	if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) {
+		rc = -EINVAL;
+		goto free_parent_pntsd;
+	}
 
 	aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2, GFP_KERNEL);
 	if (!aces_base) {
 		rc = -ENOMEM;
 		goto free_parent_pntsd;
 	}
 
 	aces = (struct smb_ace *)aces_base;
 	parent_aces = (struct smb_ace *)((char *)parent_pdacl +
 			sizeof(struct smb_acl));
+	aces_size = acl_len - sizeof(struct smb_acl);
 
 	if (pntsd_type & DACL_AUTO_INHERITED)
 		inherited_flags = INHERITED_ACE;
 
 	for (i = 0; i < num_aces; i++) {
+		int pace_size;
+
+		if (offsetof(struct smb_ace, access_req) > aces_size)
+			break;
+
+		pace_size = le16_to_cpu(parent_aces->size);
+		if (pace_size > aces_size)
+			break;
+
+		aces_size -= pace_size;
+
 		flags = parent_aces->flags;
 		if (!smb_inherit_flags(flags, is_dir))
 			goto pass;
 		if (is_dir) {
 			flags &= ~(INHERIT_ONLY_ACE | INHERITED_ACE);
@@ -1055,12 +1095,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
 			    parent_aces->access_req);
 		nt_size += le16_to_cpu(aces->size);
 		aces = (struct smb_ace *)((char *)aces + le16_to_cpu(aces->size));
 		ace_cnt++;
 pass:
-		parent_aces =
-			(struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size));
+		parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size);
 	}
 
 	if (nt_size > 0) {
 		struct smb_ntsd *pntsd;
 		struct smb_acl *pdacl;
@@ -1151,62 +1190,63 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
 {
 	struct user_namespace *user_ns = mnt_user_ns(path->mnt);
 	struct smb_ntsd *pntsd = NULL;
 	struct smb_acl *pdacl;
 	struct posix_acl *posix_acls;
-	int rc = 0, acl_size;
+	int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
 	struct smb_sid sid;
 	int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
 	struct smb_ace *ace;
 	int i, found = 0;
 	unsigned int access_bits = 0;
 	struct smb_ace *others_ace = NULL;
 	struct posix_acl_entry *pa_entry;
 	unsigned int sid_type = SIDOWNER;
-	char *end_of_acl;
+	unsigned short ace_size;
 
 	ksmbd_debug(SMB, "check permission using windows acl\n");
-	acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
-					  path->dentry, &pntsd);
-	if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) {
-		kfree(pntsd);
-		return 0;
-	}
+	pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
+					    path->dentry, &pntsd);
+	if (pntsd_size <= 0 || !pntsd)
+		goto err_out;
+
+	dacl_offset = le32_to_cpu(pntsd->dacloffset);
+	if (!dacl_offset ||
+	    (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
+		goto err_out;
 
 	pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));
-	end_of_acl = ((char *)pntsd) + acl_size;
-	if (end_of_acl <= (char *)pdacl) {
-		kfree(pntsd);
-		return 0;
-	}
+	acl_size = pntsd_size - dacl_offset;
+	pdacl_size = le16_to_cpu(pdacl->size);
 
-	if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) ||
-	    le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) {
-		kfree(pntsd);
-		return 0;
-	}
+	if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl))
+		goto err_out;
 
 	if (!pdacl->num_aces) {
-		if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) &&
+		if (!(pdacl_size - sizeof(struct smb_acl)) &&
 		    *pdaccess & ~(FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE)) {
 			rc = -EACCES;
 			goto err_out;
 		}
-		kfree(pntsd);
-		return 0;
+		goto err_out;
 	}
 
 	if (*pdaccess & FILE_MAXIMAL_ACCESS_LE) {
 		granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
 			DELETE;
 
 		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)
+				break;
+			ace_size = le16_to_cpu(ace->size);
+			if (ace_size > aces_size)
+				break;
+			aces_size -= ace_size;
 			granted |= le32_to_cpu(ace->access_req);
 			ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
-			if (end_of_acl < (char *)ace)
-				goto err_out;
 		}
 
 		if (!pdacl->num_aces)
 			granted = GENERIC_ALL_FLAGS;
 	}
@@ -1214,22 +1254,28 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
 	if (!uid)
 		sid_type = SIDUNIX_USER;
 	id_to_sid(uid, sid_type, &sid);
 
 	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)
+			break;
+		ace_size = le16_to_cpu(ace->size);
+		if (ace_size > aces_size)
+			break;
+		aces_size -= ace_size;
+
 		if (!compare_sids(&sid, &ace->sid) ||
 		    !compare_sids(&sid_unix_NFS_mode, &ace->sid)) {
 			found = 1;
 			break;
 		}
 		if (!compare_sids(&sid_everyone, &ace->sid))
 			others_ace = ace;
 
 		ace = (struct smb_ace *)((char *)ace + le16_to_cpu(ace->size));
-		if (end_of_acl < (char *)ace)
-			goto err_out;
 	}
 
 	if (*pdaccess & FILE_MAXIMAL_ACCESS_LE && found) {
 		granted = READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES |
 			DELETE;
diff --git a/fs/ksmbd/smbacl.h b/fs/ksmbd/smbacl.h
index 811af3309429..fcb2c83f2992 100644
--- a/fs/ksmbd/smbacl.h
+++ b/fs/ksmbd/smbacl.h
@@ -191,11 +191,11 @@ struct posix_acl_state {
 };
 
 int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
 		   int acl_len, struct smb_fattr *fattr);
 int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
-		   struct smb_ntsd *ppntsd, int addition_info,
+		   struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info,
 		   __u32 *secdesclen, struct smb_fattr *fattr);
 int init_acl_state(struct posix_acl_state *state, int cnt);
 void free_acl_state(struct posix_acl_state *state);
 void posix_state_to_acl(struct posix_acl_state *state,
 			struct posix_acl_entry *pace);
diff --git a/fs/ksmbd/vfs.c b/fs/ksmbd/vfs.c
index c23793469645..ea139078b4b7 100644
--- a/fs/ksmbd/vfs.c
+++ b/fs/ksmbd/vfs.c
@@ -1537,10 +1537,15 @@ int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
 		rc = -EINVAL;
 		goto out_free;
 	}
 
 	*pntsd = acl.sd_buf;
+	if (acl.sd_size < sizeof(struct smb_ntsd)) {
+		pr_err("sd size is invalid\n");
+		goto out_free;
+	}
+
 	(*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) -
 					   NDR_NTSD_OFFSETOF);
 	(*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) -
 					   NDR_NTSD_OFFSETOF);
 	(*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) -

Vulnerable code: 		if (req->InfoType == SMB2_O_INFO_FILE &&
		    (req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
		     req->FileInfoClass == FILE_ALL_INFORMATION))
							    pntsd, NULL,
	free_len = (int)(work->response_sz -
			 (get_rfc1002_len(work->response_buf) + 4)) -
		hdr2_len;
	__u32 secdesclen;
	int rc;
		ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
				       fp->filp->f_path.dentry, &ppntsd);

	rc = build_sec_desc(user_ns, pntsd, ppntsd, addition_info,
			    &secdesclen, &fattr);
			memcpy((char *)pndace + size, ntace, le16_to_cpu(ntace->size));
			size += le16_to_cpu(ntace->size);
			ntace = (struct smb_ace *)((char *)ntace + le16_to_cpu(ntace->size));
		   int addition_info, __u32 *secdesclen,
		} else if (!ppntsd->dacloffset) {
			goto out;
			ppdacl_ptr = (struct smb_acl *)((char *)ppntsd +
						le32_to_cpu(ppntsd->dacloffset));
				       nowner_sid_ptr, ngroup_sid_ptr, fattr);
	int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0;
	int rc = 0, num_aces, dacloffset, pntsd_type, acl_len;
	acl_len = ksmbd_vfs_get_sd_xattr(conn, user_ns,
					 parent, &parent_pntsd);
	if (acl_len <= 0)
	if (!dacloffset) {
		parent_aces =
			(struct smb_ace *)((char *)parent_aces + le16_to_cpu(parent_aces->size));
	int rc = 0, acl_size;
	char *end_of_acl;
	acl_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
					  path->dentry, &pntsd);
	if (acl_size <= 0 || !pntsd || !pntsd->dacloffset) {
		kfree(pntsd);
		return 0;
	}
	end_of_acl = ((char *)pntsd) + acl_size;
	if (end_of_acl <= (char *)pdacl) {
		kfree(pntsd);
		return 0;
	}
	if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size) ||
	    le16_to_cpu(pdacl->size) < sizeof(struct smb_acl)) {
		kfree(pntsd);
		return 0;
	}
		if (!(le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) &&
		kfree(pntsd);
		return 0;
			if (end_of_acl < (char *)ace)
				goto err_out;
		if (end_of_acl < (char *)ace)
			goto err_out;
		   struct smb_ntsd *ppntsd, int addition_info,
Fixed code: 		if ((req->InfoType == SMB2_O_INFO_FILE &&
		     (req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
		     req->FileInfoClass == FILE_ALL_INFORMATION)) ||
		    req->InfoType == SMB2_O_INFO_SECURITY)
							    pntsd, NULL, 0,
static int smb2_resp_buf_len(struct ksmbd_work *work, unsigned short hdr2_len)
{
	int free_len;

	free_len = (int)(work->response_sz -
		(get_rfc1002_len(work->response_buf) + 4)) - hdr2_len;
	return free_len;
}

	free_len = smb2_resp_buf_len(work, hdr2_len);
	__u32 secdesclen = 0;
	int rc = 0, ppntsd_size = 0;
		ppntsd_size = ksmbd_vfs_get_sd_xattr(work->conn, user_ns,
						     fp->filp->f_path.dentry,
						     &ppntsd);

	/* Check if sd buffer size exceeds response buffer size */
	if (smb2_resp_buf_len(work, 8) > ppntsd_size)
		rc = build_sec_desc(user_ns, pntsd, ppntsd, ppntsd_size,
				    addition_info, &secdesclen, &fattr);
			   unsigned int aces_size,
			unsigned short nt_ace_size;

			if (offsetof(struct smb_ace, access_req) > aces_size)
				break;

			nt_ace_size = le16_to_cpu(ntace->size);
			if (nt_ace_size > aces_size)
				break;

			memcpy((char *)pndace + size, ntace, nt_ace_size);
			size += nt_ace_size;
			aces_size -= nt_ace_size;
			ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
		   int ppntsd_size, int addition_info, __u32 *secdesclen,
			unsigned int dacl_offset = le32_to_cpu(ppntsd->dacloffset);
			int ppdacl_size, ntacl_size = ppntsd_size - dacl_offset;

			if (!dacl_offset ||
			    (dacl_offset + sizeof(struct smb_acl) > ppntsd_size))
				goto out;

			ppdacl_ptr = (struct smb_acl *)((char *)ppntsd + dacl_offset);
			ppdacl_size = le16_to_cpu(ppdacl_ptr->size);
			if (ppdacl_size > ntacl_size ||
			    ppdacl_size < sizeof(struct smb_acl))
				goto out;
				       ntacl_size - sizeof(struct smb_acl),
				       nowner_sid_ptr, ngroup_sid_ptr,
				       fattr);
	int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size;
	int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
	pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
					    parent, &parent_pntsd);
	if (pntsd_size <= 0)
	if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
	acl_len = pntsd_size - dacloffset;
	pdacl_size = le16_to_cpu(parent_pdacl->size);

	if (pdacl_size > acl_len || pdacl_size < sizeof(struct smb_acl)) {
		rc = -EINVAL;
		goto free_parent_pntsd;
	}
	aces_size = acl_len - sizeof(struct smb_acl);
		int pace_size;

		if (offsetof(struct smb_ace, access_req) > aces_size)
			break;

		pace_size = le16_to_cpu(parent_aces->size);
		if (pace_size > aces_size)
			break;

		aces_size -= pace_size;

		parent_aces = (struct smb_ace *)((char *)parent_aces + pace_size);
	int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
	unsigned short ace_size;
	pntsd_size = ksmbd_vfs_get_sd_xattr(conn, user_ns,
					    path->dentry, &pntsd);
	if (pntsd_size <= 0 || !pntsd)
		goto err_out;

	dacl_offset = le32_to_cpu(pntsd->dacloffset);
	if (!dacl_offset ||
	    (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
		goto err_out;
	acl_size = pntsd_size - dacl_offset;
	pdacl_size = le16_to_cpu(pdacl->size);
	if (pdacl_size > acl_size || pdacl_size < sizeof(struct smb_acl))
		goto err_out;
		if (!(pdacl_size - sizeof(struct smb_acl)) &&
		goto err_out;
		aces_size = acl_size - sizeof(struct smb_acl);
			if (offsetof(struct smb_ace, access_req) > aces_size)
				break;
			ace_size = le16_to_cpu(ace->size);
			if (ace_size > aces_size)
				break;
			aces_size -= ace_size;
	aces_size = acl_size - sizeof(struct smb_acl);
		if (offsetof(struct smb_ace, access_req) > aces_size)
			break;
		ace_size = le16_to_cpu(ace->size);
		if (ace_size > aces_size)
			break;
		aces_size -= ace_size;

		   struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info,
	if (acl.sd_size < sizeof(struct smb_ntsd)) {
		pr_err("sd size is invalid\n");
		goto out_free;
	}