summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2006-12-30 18:23:35 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-23 15:49:51 -0800
commit0fc7b9055c2069bdb2fae508cefaeef4d26f86aa (patch)
treeb6e61415aba35b9b9ef0b8cf8559d92c427b3d64
parent6ce115c0d888086716aef2b4b3cd702d3b4f060d (diff)
downloadlinux-rt-0fc7b9055c2069bdb2fae508cefaeef4d26f86aa.tar.gz
grow_buffers() infinite loop fix (CVE-2006-5757, CVE-2006-6060)
If grow_buffers() is for some reason passed a block number which wants to lie outside the maximum-addressable pagecache range (PAGE_SIZE * 4G bytes) then it will accidentally truncate `index' and will then instnatiate a page at the wrong pagecache offset. This causes __getblk_slow() to go into an infinite loop. This can happen with corrupted disks, or with software errors elsewhere. Detect that, and handle it. Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org> Signed-off-by: Chris Wright <chrisw@sous-sol.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--fs/buffer.c21
1 files changed, 19 insertions, 2 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 5b329f0e4f09..29fc99f833fe 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1179,8 +1179,21 @@ grow_buffers(struct block_device *bdev, sector_t block, int size)
} while ((size << sizebits) < PAGE_SIZE);
index = block >> sizebits;
- block = index << sizebits;
+ /*
+ * Check for a block which wants to lie outside our maximum possible
+ * pagecache index. (this comparison is done using sector_t types).
+ */
+ if (unlikely(index != block >> sizebits)) {
+ char b[BDEVNAME_SIZE];
+
+ printk(KERN_ERR "%s: requested out-of-range block %llu for "
+ "device %s\n",
+ __FUNCTION__, (unsigned long long)block,
+ bdevname(bdev, b));
+ return -EIO;
+ }
+ block = index << sizebits;
/* Create a page with the proper size buffers.. */
page = grow_dev_page(bdev, block, index, size);
if (!page)
@@ -1207,12 +1220,16 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size)
for (;;) {
struct buffer_head * bh;
+ int ret;
bh = __find_get_block(bdev, block, size);
if (bh)
return bh;
- if (!grow_buffers(bdev, block, size))
+ ret = grow_buffers(bdev, block, size);
+ if (ret < 0)
+ return NULL;
+ if (ret == 0)
free_more_memory();
}
}