]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
vfs data leak fix
authorChris Wright <chrisw@sous-sol.org>
Sat, 29 Mar 2008 07:11:13 +0000 (00:11 -0700)
committerChris Wright <chrisw@sous-sol.org>
Sat, 29 Mar 2008 07:11:13 +0000 (00:11 -0700)
queue-2.6.24/series
queue-2.6.24/vfs-fix-data-leak-in-nobh_write_end.patch [new file with mode: 0644]

index 2a3ac333003030ceb556926126a913d7ba55f1b7..c59484ebd807f8da33aa04b44161f57ac6dbf224 100644 (file)
@@ -15,3 +15,4 @@ xen-mask-out-sep-from-cpuid.patch
 xen-fix-up-setup-of-shared_info.patch
 percpu-__percpu_alloc_mask-can-dynamically-size-percpu_data-storage.patch
 alloc_percpu-fails-to-allocate-percpu-data.patch
+vfs-fix-data-leak-in-nobh_write_end.patch
diff --git a/queue-2.6.24/vfs-fix-data-leak-in-nobh_write_end.patch b/queue-2.6.24/vfs-fix-data-leak-in-nobh_write_end.patch
new file mode 100644 (file)
index 0000000..0b0a1a1
--- /dev/null
@@ -0,0 +1,120 @@
+From stable-bounces@linux.kernel.org  Sat Mar 29 00:08:19 2008
+Date: Fri, 28 Mar 2008 22:10:07 GMT
+Message-Id: <200803282210.m2SMA7VP021093@hera.kernel.org>
+From: jejb@kernel.org
+To: jejb@kernel.org, stable@kernel.org
+Subject: vfs: fix data leak in nobh_write_end()
+
+From: Dmitri Monakhov <dmonakhov@openvz.org>
+
+upstream commit: 5b41e74ad1b0bf7bc51765ae74e5dc564afc3e48
+
+Current nobh_write_end() implementation ignore partial writes(copied < len)
+case if page was fully mapped and simply mark page as Uptodate, which is
+totally wrong because area [pos+copied, pos+len) wasn't updated explicitly in
+previous write_begin call.  It simply contains garbage from pagecache and
+result in data leakage.
+
+#TEST_CASE_BEGIN:
+~~~~~~~~~~~~~~~~
+In fact issue triggered by classical testcase
+       open("/mnt/test", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
+       ftruncate(3, 409600)                    = 0
+       writev(3, [{"a", 1}, {NULL, 4095}], 2)  = 1
+##TESTCASE_SOURCE:
+~~~~~~~~~~~~~~~~~
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#include <errno.h>
+int main(int argc, char **argv)
+{
+       int fd,  ret;
+       void* p;
+       struct iovec iov[2];
+       fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666);
+       ftruncate(fd, 409600);
+       iov[0].iov_base="a";
+       iov[0].iov_len=1;
+       iov[1].iov_base=NULL;
+       iov[1].iov_len=4096;
+       ret = writev(fd, iov, sizeof(iov)/sizeof(struct iovec));
+       printf("writev  = %d, err = %d\n", ret, errno);
+       return 0;
+}
+##TESTCASE RESULT:
+~~~~~~~~~~~~~~~~~~
+[root@ts63 ~]# mount | grep mnt2
+/dev/mapper/test on /mnt2 type ext2 (rw,nobh)
+[root@ts63 ~]#  /tmp/writev /mnt2/test
+writev  = 1, err = 0
+[root@ts63 ~]# hexdump -C /mnt2/test
+
+00000000  61 65 62 6f 6f 74 00 00  f0 b9 b4 59 3a 00 00 00  |aeboot.....Y:...|
+00000010  20 00 00 00 00 00 00 00  21 00 00 00 00 00 00 00  | .......!.......|
+00000020  df df df df df df df df  df df df df df df df df  |................|
+00000030  3a 00 00 00 2a 00 00 00  21 00 00 00 00 00 00 00  |:...*...!.......|
+00000040  60 c0 8c 00 00 00 00 00  40 4a 8d 00 00 00 00 00  |`.......@J......|
+00000050  00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  |........A.......|
+00000060  74 69 6d 65 20 64 64 20  69 66 3d 2f 64 65 76 2f  |time dd if=/dev/|
+00000070  6c 6f 6f 70 30 20 20 6f  66 3d 2f 64 65 76 2f 6e  |loop0  of=/dev/n|
+skip..
+00000f50  00 00 00 00 00 00 00 00  31 00 00 00 00 00 00 00  |........1.......|
+00000f60  6d 6b 66 73 2e 65 78 74  33 20 2f 64 65 76 2f 76  |mkfs.ext3 /dev/v|
+00000f70  7a 76 67 2f 74 65 73 74  20 2d 62 34 30 39 36 00  |zvg/test -b4096.|
+00000f80  a0 fe 8c 00 00 00 00 00  21 00 00 00 00 00 00 00  |........!.......|
+00000f90  23 31 32 30 35 39 35 30  34 30 34 00 3a 00 00 00  |#1205950404.:...|
+00000fa0  20 00 8d 00 00 00 00 00  21 00 00 00 00 00 00 00  | .......!.......|
+00000fb0  d0 cf 8c 00 00 00 00 00  10 d0 8c 00 00 00 00 00  |................|
+00000fc0  00 00 00 00 00 00 00 00  41 00 00 00 00 00 00 00  |........A.......|
+00000fd0  6d 6f 75 6e 74 20 2f 64  65 76 2f 76 7a 76 67 2f  |mount /dev/vzvg/|
+00000fe0  74 65 73 74 20 20 2f 76  7a 20 2d 6f 20 64 61 74  |test  /vz -o dat|
+00000ff0  61 3d 77 72 69 74 65 62  61 63 6b 00 00 00 00 00  |a=writeback.....|
+00001000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+
+As you can see file's page contains garbage from pagecache instead of zeros.
+#TEST_CASE_END
+
+Attached patch:
+- Add sanity check BUG_ON in order to prevent incorrect usage by caller,
+  This is function invariant because page can has buffers and in no zero
+  *fadata pointer at the same time.
+- Always attach buffers to page is it is partial write case.
+- Always switch back to generic_write_end if page has buffers.
+  This is reasonable because if page already has buffer then generic_write_begin
+  was called previously.
+
+Signed-off-by: Dmitri Monakhov <dmonakhov@openvz.org>
+Reviewed-by: Nick Piggin <npiggin@suse.de>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+ fs/buffer.c |   13 ++++++-------
+ 1 file changed, 6 insertions(+), 7 deletions(-)
+
+--- a/fs/buffer.c
++++ b/fs/buffer.c
+@@ -2565,14 +2565,13 @@ int nobh_write_end(struct file *file, st
+       struct inode *inode = page->mapping->host;
+       struct buffer_head *head = fsdata;
+       struct buffer_head *bh;
++      BUG_ON(fsdata != NULL && page_has_buffers(page));
+-      if (!PageMappedToDisk(page)) {
+-              if (unlikely(copied < len) && !page_has_buffers(page))
+-                      attach_nobh_buffers(page, head);
+-              if (page_has_buffers(page))
+-                      return generic_write_end(file, mapping, pos, len,
+-                                              copied, page, fsdata);
+-      }
++      if (unlikely(copied < len) && !page_has_buffers(page))
++              attach_nobh_buffers(page, head);
++      if (page_has_buffers(page))
++              return generic_write_end(file, mapping, pos, len,
++                                      copied, page, fsdata);
+       SetPageUptodate(page);
+       set_page_dirty(page);