cifs: fix use-after-free bug in find_writable_file
[pandora-kernel.git] / fs / cifs / file.c
index c55808e..f9d2863 100644 (file)
@@ -1608,6 +1608,7 @@ refind_writable:
                        cifsFileInfo_put(inv_file);
                        spin_lock(&cifs_file_list_lock);
                        ++refind;
+                       inv_file = NULL;
                        goto refind_writable;
                }
        }
@@ -2107,7 +2108,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
 {
        unsigned int written;
        unsigned long num_pages, npages, i;
-       size_t copied, len, cur_len;
+       size_t bytes, copied, len, cur_len;
        ssize_t total_written = 0;
        struct kvec *to_send;
        struct page **pages;
@@ -2165,17 +2166,45 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
        do {
                size_t save_len = cur_len;
                for (i = 0; i < npages; i++) {
-                       copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
+                       bytes = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
                        copied = iov_iter_copy_from_user(pages[i], &it, 0,
-                                                        copied);
+                                                        bytes);
                        cur_len -= copied;
                        iov_iter_advance(&it, copied);
                        to_send[i+1].iov_base = kmap(pages[i]);
                        to_send[i+1].iov_len = copied;
+                       /*
+                        * If we didn't copy as much as we expected, then that
+                        * may mean we trod into an unmapped area. Stop copying
+                        * at that point. On the next pass through the big
+                        * loop, we'll likely end up getting a zero-length
+                        * write and bailing out of it.
+                        */
+                       if (copied < bytes)
+                               break;
                }
 
                cur_len = save_len - cur_len;
 
+               /*
+                * If we have no data to send, then that probably means that
+                * the copy above failed altogether. That's most likely because
+                * the address in the iovec was bogus. Set the rc to -EFAULT,
+                * free anything we allocated and bail out.
+                */
+               if (!cur_len) {
+                       kunmap(pages[0]);
+                       if (!total_written)
+                               total_written = -EFAULT;
+                       break;
+               }
+
+               /*
+                * i + 1 now represents the number of pages we actually used in
+                * the copy phase above.
+                */
+               npages = min(npages, i + 1);
+
                do {
                        if (open_file->invalidHandle) {
                                rc = cifs_reopen_file(open_file, false);