[PATCH] smb: client: fix OOB reads in cifs_to_posix_acl()

Frank Sorenson <[email protected]>
Newsgroups org.kernel.vger.stable,org.kernel.vger.linux-cifs
Message-ID <[email protected]>
cifs_to_posix_acl() reads the 6-byte fixed header (version,
access_entry_count, default_entry_count) before validating
size_of_data_area, causing an OOB read from truncated responses.

The ACL_TYPE_DEFAULT path then forms a pointer from access_entry_count
before checking bounds:

        pACE = &cifs_acl->ace_array[count];
        count = le16_to_cpu(cifs_acl->default_entry_count);
        size += sizeof(struct cifs_posix_ace) * count;
        if (size_of_data_area < size)
                return -EINVAL;

Validate the header before any field access, validate access ACE count
before pointer arithmetic in both branches, and validate default ACE
count before use.

Fixes: bd9684b042dc ("cifs: implement get acl method")
Cc: [email protected]
Signed-off-by: Frank Sorenson <[email protected]>
---
 fs/smb/client/cifssmb.c | 22 +++++++++-------------
 1 file changed, 9 insertions(+), 13 deletions(-)

diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index d445fbd2a0ca..522e01973fd4 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3326,31 +3326,27 @@ static void cifs_init_posix_acl(struct posix_acl_entry *ace,
 static int cifs_to_posix_acl(struct posix_acl **acl, char *src,
 			     const int acl_type, const int size_of_data_area)
 {
-	int size =  0;
+	int size =  sizeof(struct cifs_posix_acl);
 	__u16 count;
 	struct cifs_posix_ace *pACE;
 	struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
 	struct posix_acl *kacl = NULL;
 	struct posix_acl_entry *pa, *pe;
 
+	if (size_of_data_area < size) /* validate acl header */
+		return -EINVAL;
+
 	if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
 		return -EOPNOTSUPP;
 
+	count = le16_to_cpu(cifs_acl->access_entry_count);
+	size += sizeof(struct cifs_posix_ace) * count;
+	if (size_of_data_area < size) /* validate access ACEs */
+		return -EINVAL;
+
 	if (acl_type == ACL_TYPE_ACCESS) {
-		count = le16_to_cpu(cifs_acl->access_entry_count);
 		pACE = &cifs_acl->ace_array[0];
-		size = sizeof(struct cifs_posix_acl);
-		size += sizeof(struct cifs_posix_ace) * count;
-		/* check if we would go beyond end of SMB */
-		if (size_of_data_area < size) {
-			cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n",
-				 size_of_data_area, size);
-			return -EINVAL;
-		}
 	} else if (acl_type == ACL_TYPE_DEFAULT) {
-		count = le16_to_cpu(cifs_acl->access_entry_count);
-		size = sizeof(struct cifs_posix_acl);
-		size += sizeof(struct cifs_posix_ace) * count;
 		/* skip past access ACEs to get to default ACEs */
 		pACE = &cifs_acl->ace_array[count];
 		count = le16_to_cpu(cifs_acl->default_entry_count);
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.