1
0
Fork 0

[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>
hifive-unleashed-5.1
Andrew Morton 2006-10-11 01:21:46 -07:00 committed by Linus Torvalds
parent e0ab2928cc
commit e565793386
1 changed files with 19 additions and 2 deletions

View File

@ -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();
}
}