diff options
author | Bob Copeland <me@bobcopeland.com> | 2008-08-15 03:40:47 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-08-15 11:35:44 -0400 |
commit | 9419fc1c957d600093baaea247fef23cca3b4e93 (patch) | |
tree | 532606ac97d86d8952ffcdd8f8513b1499c10bf8 /fs/omfs | |
parent | c963343a1150106819773e828c9b237ed977615b (diff) |
omfs: fix oops when file metadata is corrupted
A fuzzed fileystem image failed with OMFS when the extent count was
used in a loop without being checked against the max number of extents.
It also provoked a signed division for an array index that was checked
as if unsigned, leading to index by -1.
omfsck will be updated to fix these cases, in the meantime bail out
gracefully.
Reported-by: Eric Sesterhenn <snakebyte@gmx.de>
Signed-off-by: Bob Copeland <me@bobcopeland.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/omfs')
-rw-r--r-- | fs/omfs/bitmap.c | 5 | ||||
-rw-r--r-- | fs/omfs/file.c | 33 |
2 files changed, 29 insertions, 9 deletions
diff --git a/fs/omfs/bitmap.c b/fs/omfs/bitmap.c index 697663b01bae..e1c0ec0ae989 100644 --- a/fs/omfs/bitmap.c +++ b/fs/omfs/bitmap.c | |||
@@ -92,7 +92,7 @@ int omfs_allocate_block(struct super_block *sb, u64 block) | |||
92 | struct buffer_head *bh; | 92 | struct buffer_head *bh; |
93 | struct omfs_sb_info *sbi = OMFS_SB(sb); | 93 | struct omfs_sb_info *sbi = OMFS_SB(sb); |
94 | int bits_per_entry = 8 * sb->s_blocksize; | 94 | int bits_per_entry = 8 * sb->s_blocksize; |
95 | int map, bit; | 95 | unsigned int map, bit; |
96 | int ret = 0; | 96 | int ret = 0; |
97 | u64 tmp; | 97 | u64 tmp; |
98 | 98 | ||
@@ -176,7 +176,8 @@ int omfs_clear_range(struct super_block *sb, u64 block, int count) | |||
176 | struct omfs_sb_info *sbi = OMFS_SB(sb); | 176 | struct omfs_sb_info *sbi = OMFS_SB(sb); |
177 | int bits_per_entry = 8 * sb->s_blocksize; | 177 | int bits_per_entry = 8 * sb->s_blocksize; |
178 | u64 tmp; | 178 | u64 tmp; |
179 | int map, bit, ret; | 179 | unsigned int map, bit; |
180 | int ret; | ||
180 | 181 | ||
181 | tmp = block; | 182 | tmp = block; |
182 | bit = do_div(tmp, bits_per_entry); | 183 | bit = do_div(tmp, bits_per_entry); |
diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 7e2499053e4d..834b2331f6b3 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c | |||
@@ -26,6 +26,13 @@ static int omfs_sync_file(struct file *file, struct dentry *dentry, | |||
26 | return err ? -EIO : 0; | 26 | return err ? -EIO : 0; |
27 | } | 27 | } |
28 | 28 | ||
29 | static u32 omfs_max_extents(struct omfs_sb_info *sbi, int offset) | ||
30 | { | ||
31 | return (sbi->s_sys_blocksize - offset - | ||
32 | sizeof(struct omfs_extent)) / | ||
33 | sizeof(struct omfs_extent_entry) + 1; | ||
34 | } | ||
35 | |||
29 | void omfs_make_empty_table(struct buffer_head *bh, int offset) | 36 | void omfs_make_empty_table(struct buffer_head *bh, int offset) |
30 | { | 37 | { |
31 | struct omfs_extent *oe = (struct omfs_extent *) &bh->b_data[offset]; | 38 | struct omfs_extent *oe = (struct omfs_extent *) &bh->b_data[offset]; |
@@ -45,6 +52,7 @@ int omfs_shrink_inode(struct inode *inode) | |||
45 | struct buffer_head *bh; | 52 | struct buffer_head *bh; |
46 | u64 next, last; | 53 | u64 next, last; |
47 | u32 extent_count; | 54 | u32 extent_count; |
55 | u32 max_extents; | ||
48 | int ret; | 56 | int ret; |
49 | 57 | ||
50 | /* traverse extent table, freeing each entry that is greater | 58 | /* traverse extent table, freeing each entry that is greater |
@@ -62,15 +70,18 @@ int omfs_shrink_inode(struct inode *inode) | |||
62 | goto out; | 70 | goto out; |
63 | 71 | ||
64 | oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]); | 72 | oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]); |
73 | max_extents = omfs_max_extents(sbi, OMFS_EXTENT_START); | ||
65 | 74 | ||
66 | for (;;) { | 75 | for (;;) { |
67 | 76 | ||
68 | if (omfs_is_bad(sbi, (struct omfs_header *) bh->b_data, next)) { | 77 | if (omfs_is_bad(sbi, (struct omfs_header *) bh->b_data, next)) |
69 | brelse(bh); | 78 | goto out_brelse; |
70 | goto out; | ||
71 | } | ||
72 | 79 | ||
73 | extent_count = be32_to_cpu(oe->e_extent_count); | 80 | extent_count = be32_to_cpu(oe->e_extent_count); |
81 | |||
82 | if (extent_count > max_extents) | ||
83 | goto out_brelse; | ||
84 | |||
74 | last = next; | 85 | last = next; |
75 | next = be64_to_cpu(oe->e_next); | 86 | next = be64_to_cpu(oe->e_next); |
76 | entry = &oe->e_entry; | 87 | entry = &oe->e_entry; |
@@ -98,10 +109,14 @@ int omfs_shrink_inode(struct inode *inode) | |||
98 | if (!bh) | 109 | if (!bh) |
99 | goto out; | 110 | goto out; |
100 | oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); | 111 | oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); |
112 | max_extents = omfs_max_extents(sbi, OMFS_EXTENT_CONT); | ||
101 | } | 113 | } |
102 | ret = 0; | 114 | ret = 0; |
103 | out: | 115 | out: |
104 | return ret; | 116 | return ret; |
117 | out_brelse: | ||
118 | brelse(bh); | ||
119 | return ret; | ||
105 | } | 120 | } |
106 | 121 | ||
107 | static void omfs_truncate(struct inode *inode) | 122 | static void omfs_truncate(struct inode *inode) |
@@ -154,9 +169,7 @@ static int omfs_grow_extent(struct inode *inode, struct omfs_extent *oe, | |||
154 | goto out; | 169 | goto out; |
155 | } | 170 | } |
156 | } | 171 | } |
157 | max_count = (sbi->s_sys_blocksize - OMFS_EXTENT_START - | 172 | max_count = omfs_max_extents(sbi, OMFS_EXTENT_START); |
158 | sizeof(struct omfs_extent)) / | ||
159 | sizeof(struct omfs_extent_entry) + 1; | ||
160 | 173 | ||
161 | /* TODO: add a continuation block here */ | 174 | /* TODO: add a continuation block here */ |
162 | if (be32_to_cpu(oe->e_extent_count) > max_count-1) | 175 | if (be32_to_cpu(oe->e_extent_count) > max_count-1) |
@@ -225,6 +238,7 @@ static int omfs_get_block(struct inode *inode, sector_t block, | |||
225 | sector_t next, offset; | 238 | sector_t next, offset; |
226 | int ret; | 239 | int ret; |
227 | u64 new_block; | 240 | u64 new_block; |
241 | u32 max_extents; | ||
228 | int extent_count; | 242 | int extent_count; |
229 | struct omfs_extent *oe; | 243 | struct omfs_extent *oe; |
230 | struct omfs_extent_entry *entry; | 244 | struct omfs_extent_entry *entry; |
@@ -238,6 +252,7 @@ static int omfs_get_block(struct inode *inode, sector_t block, | |||
238 | goto out; | 252 | goto out; |
239 | 253 | ||
240 | oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]); | 254 | oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]); |
255 | max_extents = omfs_max_extents(sbi, OMFS_EXTENT_START); | ||
241 | next = inode->i_ino; | 256 | next = inode->i_ino; |
242 | 257 | ||
243 | for (;;) { | 258 | for (;;) { |
@@ -249,6 +264,9 @@ static int omfs_get_block(struct inode *inode, sector_t block, | |||
249 | next = be64_to_cpu(oe->e_next); | 264 | next = be64_to_cpu(oe->e_next); |
250 | entry = &oe->e_entry; | 265 | entry = &oe->e_entry; |
251 | 266 | ||
267 | if (extent_count > max_extents) | ||
268 | goto out_brelse; | ||
269 | |||
252 | offset = find_block(inode, entry, block, extent_count, &remain); | 270 | offset = find_block(inode, entry, block, extent_count, &remain); |
253 | if (offset > 0) { | 271 | if (offset > 0) { |
254 | ret = 0; | 272 | ret = 0; |
@@ -266,6 +284,7 @@ static int omfs_get_block(struct inode *inode, sector_t block, | |||
266 | if (!bh) | 284 | if (!bh) |
267 | goto out; | 285 | goto out; |
268 | oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); | 286 | oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); |
287 | max_extents = omfs_max_extents(sbi, OMFS_EXTENT_CONT); | ||
269 | } | 288 | } |
270 | if (create) { | 289 | if (create) { |
271 | ret = omfs_grow_extent(inode, oe, &new_block); | 290 | ret = omfs_grow_extent(inode, oe, &new_block); |