]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.4.83/cifs-ensure-that-uncached-writes-handle-unmapped-areas-correctly.patch
5.1-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.4.83 / cifs-ensure-that-uncached-writes-handle-unmapped-areas-correctly.patch
1 From 5d81de8e8667da7135d3a32a964087c0faf5483f Mon Sep 17 00:00:00 2001
2 From: Jeff Layton <jlayton@redhat.com>
3 Date: Fri, 14 Feb 2014 07:20:35 -0500
4 Subject: cifs: ensure that uncached writes handle unmapped areas correctly
5
6 From: Jeff Layton <jlayton@redhat.com>
7
8 commit 5d81de8e8667da7135d3a32a964087c0faf5483f upstream.
9
10 It's possible for userland to pass down an iovec via writev() that has a
11 bogus user pointer in it. If that happens and we're doing an uncached
12 write, then we can end up getting less bytes than we expect from the
13 call to iov_iter_copy_from_user. This is CVE-2014-0069
14
15 cifs_iovec_write isn't set up to handle that situation however. It'll
16 blindly keep chugging through the page array and not filling those pages
17 with anything useful. Worse yet, we'll later end up with a negative
18 number in wdata->tailsz, which will confuse the sending routines and
19 cause an oops at the very least.
20
21 Fix this by having the copy phase of cifs_iovec_write stop copying data
22 in this situation and send the last write as a short one. At the same
23 time, we want to avoid sending a zero-length write to the server, so
24 break out of the loop and set rc to -EFAULT if that happens. This also
25 allows us to handle the case where no address in the iovec is valid.
26
27 [Note: Marking this for stable on v3.4+ kernels, but kernels as old as
28 v2.6.38 may have a similar problem and may need similar fix]
29
30 Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru>
31 Reported-by: Al Viro <viro@zeniv.linux.org.uk>
32 Signed-off-by: Jeff Layton <jlayton@redhat.com>
33 Signed-off-by: Steve French <smfrench@gmail.com>
34 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
35
36 ---
37 fs/cifs/file.c | 37 ++++++++++++++++++++++++++++++++++---
38 1 file changed, 34 insertions(+), 3 deletions(-)
39
40 --- a/fs/cifs/file.c
41 +++ b/fs/cifs/file.c
42 @@ -2185,7 +2185,7 @@ cifs_iovec_write(struct file *file, cons
43 unsigned long nr_segs, loff_t *poffset)
44 {
45 unsigned long nr_pages, i;
46 - size_t copied, len, cur_len;
47 + size_t bytes, copied, len, cur_len;
48 ssize_t total_written = 0;
49 loff_t offset;
50 struct iov_iter it;
51 @@ -2236,14 +2236,45 @@ cifs_iovec_write(struct file *file, cons
52
53 save_len = cur_len;
54 for (i = 0; i < nr_pages; i++) {
55 - copied = min_t(const size_t, cur_len, PAGE_SIZE);
56 + bytes = min_t(const size_t, cur_len, PAGE_SIZE);
57 copied = iov_iter_copy_from_user(wdata->pages[i], &it,
58 - 0, copied);
59 + 0, bytes);
60 cur_len -= copied;
61 iov_iter_advance(&it, copied);
62 + /*
63 + * If we didn't copy as much as we expected, then that
64 + * may mean we trod into an unmapped area. Stop copying
65 + * at that point. On the next pass through the big
66 + * loop, we'll likely end up getting a zero-length
67 + * write and bailing out of it.
68 + */
69 + if (copied < bytes)
70 + break;
71 }
72 cur_len = save_len - cur_len;
73
74 + /*
75 + * If we have no data to send, then that probably means that
76 + * the copy above failed altogether. That's most likely because
77 + * the address in the iovec was bogus. Set the rc to -EFAULT,
78 + * free anything we allocated and bail out.
79 + */
80 + if (!cur_len) {
81 + for (i = 0; i < nr_pages; i++)
82 + put_page(wdata->pages[i]);
83 + kfree(wdata);
84 + rc = -EFAULT;
85 + break;
86 + }
87 +
88 + /*
89 + * i + 1 now represents the number of pages we actually used in
90 + * the copy phase above. Bring nr_pages down to that, and free
91 + * any pages that we didn't use.
92 + */
93 + for ( ; nr_pages > i + 1; nr_pages--)
94 + put_page(wdata->pages[nr_pages - 1]);
95 +
96 wdata->sync_mode = WB_SYNC_ALL;
97 wdata->nr_pages = nr_pages;
98 wdata->offset = (__u64)offset;