Commit e5657933 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] grow_buffers() infinite loop fix

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: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e0ab2928
...@@ -1042,8 +1042,21 @@ grow_buffers(struct block_device *bdev, sector_t block, int size) ...@@ -1042,8 +1042,21 @@ grow_buffers(struct block_device *bdev, sector_t block, int size)
} while ((size << sizebits) < PAGE_SIZE); } while ((size << sizebits) < PAGE_SIZE);
index = block >> sizebits; 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.. */ /* Create a page with the proper size buffers.. */
page = grow_dev_page(bdev, block, index, size); page = grow_dev_page(bdev, block, index, size);
if (!page) if (!page)
...@@ -1070,12 +1083,16 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size) ...@@ -1070,12 +1083,16 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size)
for (;;) { for (;;) {
struct buffer_head * bh; struct buffer_head * bh;
int ret;
bh = __find_get_block(bdev, block, size); bh = __find_get_block(bdev, block, size);
if (bh) if (bh)
return 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(); free_more_memory();
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment