Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
@@ -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();
	}
}