diff options
author | OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> | 2018-08-22 00:59:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-22 13:52:50 -0400 |
commit | 0afa9626667c3659ef8bd82d42a11e39fedf235c (patch) | |
tree | 9b5c96147d7eb18dc6d6b148e2f02c76b8ef9053 | |
parent | f663b5b38fffeb31841f8bfaf0ef87a445b0ffee (diff) |
fat: validate ->i_start before using
On corrupted FATfs may have invalid ->i_start. To handle it, this checks
->i_start before using, and return proper error code.
Link: http://lkml.kernel.org/r/87o9f8y1t5.fsf_-_@mail.parknet.co.jp
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Reported-by: Anatoly Trosinenko <anatoly.trosinenko@gmail.com>
Tested-by: Anatoly Trosinenko <anatoly.trosinenko@gmail.com>
Cc: Alan Cox <gnomes@lxorguk.ukuu.org.uk>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/fat/cache.c | 19 | ||||
-rw-r--r-- | fs/fat/fat.h | 5 | ||||
-rw-r--r-- | fs/fat/fatent.c | 6 |
3 files changed, 20 insertions, 10 deletions
diff --git a/fs/fat/cache.c b/fs/fat/cache.c index e9bed49df6b7..78d501c1fb65 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c | |||
@@ -225,7 +225,8 @@ static inline void cache_init(struct fat_cache_id *cid, int fclus, int dclus) | |||
225 | int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus) | 225 | int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus) |
226 | { | 226 | { |
227 | struct super_block *sb = inode->i_sb; | 227 | struct super_block *sb = inode->i_sb; |
228 | const int limit = sb->s_maxbytes >> MSDOS_SB(sb)->cluster_bits; | 228 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
229 | const int limit = sb->s_maxbytes >> sbi->cluster_bits; | ||
229 | struct fat_entry fatent; | 230 | struct fat_entry fatent; |
230 | struct fat_cache_id cid; | 231 | struct fat_cache_id cid; |
231 | int nr; | 232 | int nr; |
@@ -234,6 +235,12 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus) | |||
234 | 235 | ||
235 | *fclus = 0; | 236 | *fclus = 0; |
236 | *dclus = MSDOS_I(inode)->i_start; | 237 | *dclus = MSDOS_I(inode)->i_start; |
238 | if (!fat_valid_entry(sbi, *dclus)) { | ||
239 | fat_fs_error_ratelimit(sb, | ||
240 | "%s: invalid start cluster (i_pos %lld, start %08x)", | ||
241 | __func__, MSDOS_I(inode)->i_pos, *dclus); | ||
242 | return -EIO; | ||
243 | } | ||
237 | if (cluster == 0) | 244 | if (cluster == 0) |
238 | return 0; | 245 | return 0; |
239 | 246 | ||
@@ -250,9 +257,8 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus) | |||
250 | /* prevent the infinite loop of cluster chain */ | 257 | /* prevent the infinite loop of cluster chain */ |
251 | if (*fclus > limit) { | 258 | if (*fclus > limit) { |
252 | fat_fs_error_ratelimit(sb, | 259 | fat_fs_error_ratelimit(sb, |
253 | "%s: detected the cluster chain loop" | 260 | "%s: detected the cluster chain loop (i_pos %lld)", |
254 | " (i_pos %lld)", __func__, | 261 | __func__, MSDOS_I(inode)->i_pos); |
255 | MSDOS_I(inode)->i_pos); | ||
256 | nr = -EIO; | 262 | nr = -EIO; |
257 | goto out; | 263 | goto out; |
258 | } | 264 | } |
@@ -262,9 +268,8 @@ int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus) | |||
262 | goto out; | 268 | goto out; |
263 | else if (nr == FAT_ENT_FREE) { | 269 | else if (nr == FAT_ENT_FREE) { |
264 | fat_fs_error_ratelimit(sb, | 270 | fat_fs_error_ratelimit(sb, |
265 | "%s: invalid cluster chain (i_pos %lld)", | 271 | "%s: invalid cluster chain (i_pos %lld)", |
266 | __func__, | 272 | __func__, MSDOS_I(inode)->i_pos); |
267 | MSDOS_I(inode)->i_pos); | ||
268 | nr = -EIO; | 273 | nr = -EIO; |
269 | goto out; | 274 | goto out; |
270 | } else if (nr == FAT_ENT_EOF) { | 275 | } else if (nr == FAT_ENT_EOF) { |
diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 154ae54a6b3a..df84d5710b59 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h | |||
@@ -348,6 +348,11 @@ static inline void fatent_brelse(struct fat_entry *fatent) | |||
348 | fatent->fat_inode = NULL; | 348 | fatent->fat_inode = NULL; |
349 | } | 349 | } |
350 | 350 | ||
351 | static inline bool fat_valid_entry(struct msdos_sb_info *sbi, int entry) | ||
352 | { | ||
353 | return FAT_START_ENT <= entry && entry < sbi->max_cluster; | ||
354 | } | ||
355 | |||
351 | extern void fat_ent_access_init(struct super_block *sb); | 356 | extern void fat_ent_access_init(struct super_block *sb); |
352 | extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent, | 357 | extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent, |
353 | int entry); | 358 | int entry); |
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 25d43a5e8a4d..defc2168de91 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c | |||
@@ -24,7 +24,7 @@ static void fat12_ent_blocknr(struct super_block *sb, int entry, | |||
24 | { | 24 | { |
25 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 25 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
26 | int bytes = entry + (entry >> 1); | 26 | int bytes = entry + (entry >> 1); |
27 | WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry); | 27 | WARN_ON(!fat_valid_entry(sbi, entry)); |
28 | *offset = bytes & (sb->s_blocksize - 1); | 28 | *offset = bytes & (sb->s_blocksize - 1); |
29 | *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits); | 29 | *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits); |
30 | } | 30 | } |
@@ -34,7 +34,7 @@ static void fat_ent_blocknr(struct super_block *sb, int entry, | |||
34 | { | 34 | { |
35 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 35 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
36 | int bytes = (entry << sbi->fatent_shift); | 36 | int bytes = (entry << sbi->fatent_shift); |
37 | WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry); | 37 | WARN_ON(!fat_valid_entry(sbi, entry)); |
38 | *offset = bytes & (sb->s_blocksize - 1); | 38 | *offset = bytes & (sb->s_blocksize - 1); |
39 | *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits); | 39 | *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits); |
40 | } | 40 | } |
@@ -354,7 +354,7 @@ int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry) | |||
354 | int err, offset; | 354 | int err, offset; |
355 | sector_t blocknr; | 355 | sector_t blocknr; |
356 | 356 | ||
357 | if (entry < FAT_START_ENT || sbi->max_cluster <= entry) { | 357 | if (!fat_valid_entry(sbi, entry)) { |
358 | fatent_brelse(fatent); | 358 | fatent_brelse(fatent); |
359 | fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry); | 359 | fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry); |
360 | return -EIO; | 360 | return -EIO; |