summary refs log tree commit diff
path: root/fs/cifs/file.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-02-17 13:50:11 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2014-02-17 13:50:11 -0800
commit351a7934c0e788262f83380188f18e575f6bc63f (patch)
treee9edae6b05459ce1aa4f045be5eb53f705d026ae /fs/cifs/file.c
parent7026f1929e18921fd67bf478f475a8fdfdff16ae (diff)
parent2365c4eaf077c48574ab6f143960048fc0f31518 (diff)
downloadlinux-351a7934c0e788262f83380188f18e575f6bc63f.tar.gz
Merge branch 'for-linus' of git://git.samba.org/sfrench/cifs-2.6
Pull CIFS fixes from Steve French:
 "Three cifs fixes, the most important fixing the problem with passing
  bogus pointers with writev (CVE-2014-0069).

  Two additional cifs fixes are still in review (including the fix for
  an append problem which Al also discovered)"

* 'for-linus' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: Fix too big maxBuf size for SMB3 mounts
  cifs: ensure that uncached writes handle unmapped areas correctly
  [CIFS] Fix cifsacl mounts over smb2 to not call cifs
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r--fs/cifs/file.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 755584684f6c..53c15074bb36 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -244,7 +244,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
 					      xid);
 	else
 		rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb,
-					 xid, &fid->netfid);
+					 xid, fid);
 
 out:
 	kfree(buf);
@@ -2389,7 +2389,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
 		 unsigned long nr_segs, loff_t *poffset)
 {
 	unsigned long nr_pages, i;
-	size_t copied, len, cur_len;
+	size_t bytes, copied, len, cur_len;
 	ssize_t total_written = 0;
 	loff_t offset;
 	struct iov_iter it;
@@ -2444,14 +2444,45 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
 
 		save_len = cur_len;
 		for (i = 0; i < nr_pages; i++) {
-			copied = min_t(const size_t, cur_len, PAGE_SIZE);
+			bytes = min_t(const size_t, cur_len, PAGE_SIZE);
 			copied = iov_iter_copy_from_user(wdata->pages[i], &it,
-							 0, copied);
+							 0, bytes);
 			cur_len -= copied;
 			iov_iter_advance(&it, 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) {
+			for (i = 0; i < nr_pages; i++)
+				put_page(wdata->pages[i]);
+			kfree(wdata);
+			rc = -EFAULT;
+			break;
+		}
+
+		/*
+		 * i + 1 now represents the number of pages we actually used in
+		 * the copy phase above. Bring nr_pages down to that, and free
+		 * any pages that we didn't use.
+		 */
+		for ( ; nr_pages > i + 1; nr_pages--)
+			put_page(wdata->pages[nr_pages - 1]);
+
 		wdata->sync_mode = WB_SYNC_ALL;
 		wdata->nr_pages = nr_pages;
 		wdata->offset = (__u64)offset;