CIFS: fix wrapping bugs in num_entries()
authorDan Carpenter <dan.carpenter@oracle.com>
Thu, 6 Sep 2018 09:48:22 +0000 (12:48 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Sep 2018 06:38:08 +0000 (08:38 +0200)
commit 56446f218af1133c802dad8e9e116f07f381846c upstream.

The problem is that "entryptr + next_offset" and "entryptr + len + size"
can wrap.  I ended up changing the type of "entryptr" because it makes
the math easier when we don't have to do so much casting.

Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
CC: Stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/cifs/smb2pdu.c

index 078ec70..6930953 100644 (file)
@@ -2939,33 +2939,38 @@ num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
        int len;
        unsigned int entrycount = 0;
        unsigned int next_offset = 0;
-       FILE_DIRECTORY_INFO *entryptr;
+       char *entryptr;
+       FILE_DIRECTORY_INFO *dir_info;
 
        if (bufstart == NULL)
                return 0;
 
-       entryptr = (FILE_DIRECTORY_INFO *)bufstart;
+       entryptr = bufstart;
 
        while (1) {
-               entryptr = (FILE_DIRECTORY_INFO *)
-                                       ((char *)entryptr + next_offset);
-
-               if ((char *)entryptr + size > end_of_buf) {
+               if (entryptr + next_offset < entryptr ||
+                   entryptr + next_offset > end_of_buf ||
+                   entryptr + next_offset + size > end_of_buf) {
                        cifs_dbg(VFS, "malformed search entry would overflow\n");
                        break;
                }
 
-               len = le32_to_cpu(entryptr->FileNameLength);
-               if ((char *)entryptr + len + size > end_of_buf) {
+               entryptr = entryptr + next_offset;
+               dir_info = (FILE_DIRECTORY_INFO *)entryptr;
+
+               len = le32_to_cpu(dir_info->FileNameLength);
+               if (entryptr + len < entryptr ||
+                   entryptr + len > end_of_buf ||
+                   entryptr + len + size > end_of_buf) {
                        cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n",
                                 end_of_buf);
                        break;
                }
 
-               *lastentry = (char *)entryptr;
+               *lastentry = entryptr;
                entrycount++;
 
-               next_offset = le32_to_cpu(entryptr->NextEntryOffset);
+               next_offset = le32_to_cpu(dir_info->NextEntryOffset);
                if (!next_offset)
                        break;
        }