diff options
author | Andrew Morton <akpm@osdl.org> | 2006-10-11 04:21:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-11 14:14:21 -0400 |
commit | e5657933863f43cc6bb76a54d659303dafaa9e58 (patch) | |
tree | 7f6351194953b0d167ea79749d9fcaf9ae7f6a1f /fs/buffer.c | |
parent | e0ab2928cc2202f13f0574d4c6f567f166d307eb (diff) |
[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>
Diffstat (limited to 'fs/buffer.c')
-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 eeb8ac1aa856..2a7828c0e59b 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
@@ -1042,8 +1042,21 @@ grow_buffers(struct block_device *bdev, sector_t block, int size) | |||
1042 | } while ((size << sizebits) < PAGE_SIZE); | 1042 | } while ((size << sizebits) < PAGE_SIZE); |
1043 | 1043 | ||
1044 | index = block >> sizebits; | 1044 | index = block >> sizebits; |
1045 | block = index << sizebits; | ||
1046 | 1045 | ||
1046 | /* | ||
1047 | * Check for a block which wants to lie outside our maximum possible | ||
1048 | * pagecache index. (this comparison is done using sector_t types). | ||
1049 | */ | ||
1050 | if (unlikely(index != block >> sizebits)) { | ||
1051 | char b[BDEVNAME_SIZE]; | ||
1052 | |||
1053 | printk(KERN_ERR "%s: requested out-of-range block %llu for " | ||
1054 | "device %s\n", | ||
1055 | __FUNCTION__, (unsigned long long)block, | ||
1056 | bdevname(bdev, b)); | ||
1057 | return -EIO; | ||
1058 | } | ||
1059 | block = index << sizebits; | ||
1047 | /* Create a page with the proper size buffers.. */ | 1060 | /* Create a page with the proper size buffers.. */ |
1048 | page = grow_dev_page(bdev, block, index, size); | 1061 | page = grow_dev_page(bdev, block, index, size); |
1049 | if (!page) | 1062 | if (!page) |
@@ -1070,12 +1083,16 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size) | |||
1070 | 1083 | ||
1071 | for (;;) { | 1084 | for (;;) { |
1072 | struct buffer_head * bh; | 1085 | struct buffer_head * bh; |
1086 | int ret; | ||
1073 | 1087 | ||
1074 | bh = __find_get_block(bdev, block, size); | 1088 | bh = __find_get_block(bdev, block, size); |
1075 | if (bh) | 1089 | if (bh) |
1076 | return bh; | 1090 | return bh; |
1077 | 1091 | ||
1078 | if (!grow_buffers(bdev, block, size)) | 1092 | ret = grow_buffers(bdev, block, size); |
1093 | if (ret < 0) | ||
1094 | return NULL; | ||
1095 | if (ret == 0) | ||
1079 | free_more_memory(); | 1096 | free_more_memory(); |
1080 | } | 1097 | } |
1081 | } | 1098 | } |