| From stable-bounces@linux.kernel.org Sat Dec 30 15:29:08 2006 |
| Message-ID: <4596F4F7.3010709@gentoo.org> |
| Date: Sat, 30 Dec 2006 18:23:35 -0500 |
| From: Daniel Drake <dsd@gentoo.org> |
| To: stable@kernel.org |
| Cc: sandeen@redhat.com |
| Subject: grow_buffers() infinite loop fix (CVE-2006-5757, CVE-2006-6060) |
| |
| From: Andrew Morton <akpm@osdl.org> |
| |
| 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> |
| |
| --- |
| fs/buffer.c | 21 +++++++++++++++++++-- |
| 1 file changed, 19 insertions(+), 2 deletions(-) |
| |
| --- linux-2.6.18.7.orig/fs/buffer.c |
| +++ linux-2.6.18.7/fs/buffer.c |
| @@ -1179,8 +1179,21 @@ grow_buffers(struct block_device *bdev, |
| } 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, |
| |
| 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(); |
| } |
| } |