diff options
author | Andrew Morton <akpm@osdl.org> | 2006-12-30 18:23:35 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-23 15:49:51 -0800 |
commit | 0fc7b9055c2069bdb2fae508cefaeef4d26f86aa (patch) | |
tree | b6e61415aba35b9b9ef0b8cf8559d92c427b3d64 | |
parent | 6ce115c0d888086716aef2b4b3cd702d3b4f060d (diff) |
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.c | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index 5b329f0e4f0..29fc99f833f 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(); } } |