[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: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
e0ab2928cc
commit
e565793386
21
fs/buffer.c
21
fs/buffer.c
|
@ -1042,8 +1042,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)
|
||||
|
@ -1070,12 +1083,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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue