From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-eopbgr730096.outbound.protection.outlook.com ([40.107.73.96]:36990 "EHLO NAM05-DM3-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727197AbeH3WEr (ORCPT ); Thu, 30 Aug 2018 18:04:47 -0400 From: Sasha Levin To: "stable@vger.kernel.org" CC: OGAWA Hirofumi , Alan Cox , Al Viro , Andrew Morton , Linus Torvalds , Sasha Levin Subject: [PATCH AUTOSEL 4.18 012/113] fat: validate ->i_start before using Date: Thu, 30 Aug 2018 18:01:26 +0000 Message-ID: <20180830180050.35735-12-alexander.levin@microsoft.com> References: <20180830180050.35735-1-alexander.levin@microsoft.com> In-Reply-To: <20180830180050.35735-1-alexander.levin@microsoft.com> Content-Language: en-US Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Sender: stable-owner@vger.kernel.org List-ID: From: OGAWA Hirofumi [ Upstream commit 0afa9626667c3659ef8bd82d42a11e39fedf235c ] 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 Reported-by: Anatoly Trosinenko Tested-by: Anatoly Trosinenko Cc: Alan Cox Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin --- fs/fat/cache.c | 19 ++++++++++++------- fs/fat/fat.h | 5 +++++ 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) int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dcl= us) { struct super_block *sb =3D inode->i_sb; - const int limit =3D sb->s_maxbytes >> MSDOS_SB(sb)->cluster_bits; + struct msdos_sb_info *sbi =3D MSDOS_SB(sb); + const int limit =3D sb->s_maxbytes >> sbi->cluster_bits; struct fat_entry fatent; struct fat_cache_id cid; int nr; @@ -234,6 +235,12 @@ int fat_get_cluster(struct inode *inode, int cluster, = int *fclus, int *dclus) =20 *fclus =3D 0; *dclus =3D MSDOS_I(inode)->i_start; + if (!fat_valid_entry(sbi, *dclus)) { + fat_fs_error_ratelimit(sb, + "%s: invalid start cluster (i_pos %lld, start %08x)", + __func__, MSDOS_I(inode)->i_pos, *dclus); + return -EIO; + } if (cluster =3D=3D 0) return 0; =20 @@ -250,9 +257,8 @@ int fat_get_cluster(struct inode *inode, int cluster, i= nt *fclus, int *dclus) /* prevent the infinite loop of cluster chain */ if (*fclus > limit) { fat_fs_error_ratelimit(sb, - "%s: detected the cluster chain loop" - " (i_pos %lld)", __func__, - MSDOS_I(inode)->i_pos); + "%s: detected the cluster chain loop (i_pos %lld)", + __func__, MSDOS_I(inode)->i_pos); nr =3D -EIO; goto out; } @@ -262,9 +268,8 @@ int fat_get_cluster(struct inode *inode, int cluster, i= nt *fclus, int *dclus) goto out; else if (nr =3D=3D FAT_ENT_FREE) { fat_fs_error_ratelimit(sb, - "%s: invalid cluster chain (i_pos %lld)", - __func__, - MSDOS_I(inode)->i_pos); + "%s: invalid cluster chain (i_pos %lld)", + __func__, MSDOS_I(inode)->i_pos); nr =3D -EIO; goto out; } else if (nr =3D=3D FAT_ENT_EOF) { diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 8fc1093da47d..a0a00f3734bc 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -348,6 +348,11 @@ static inline void fatent_brelse(struct fat_entry *fat= ent) fatent->fat_inode =3D NULL; } =20 +static inline bool fat_valid_entry(struct msdos_sb_info *sbi, int entry) +{ + return FAT_START_ENT <=3D entry && entry < sbi->max_cluster; +} + extern void fat_ent_access_init(struct super_block *sb); extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry); diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index bac10de678cc..3aef8630a4b9 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -23,7 +23,7 @@ static void fat12_ent_blocknr(struct super_block *sb, int= entry, { struct msdos_sb_info *sbi =3D MSDOS_SB(sb); int bytes =3D entry + (entry >> 1); - WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <=3D entry); + WARN_ON(!fat_valid_entry(sbi, entry)); *offset =3D bytes & (sb->s_blocksize - 1); *blocknr =3D sbi->fat_start + (bytes >> sb->s_blocksize_bits); } @@ -33,7 +33,7 @@ static void fat_ent_blocknr(struct super_block *sb, int e= ntry, { struct msdos_sb_info *sbi =3D MSDOS_SB(sb); int bytes =3D (entry << sbi->fatent_shift); - WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <=3D entry); + WARN_ON(!fat_valid_entry(sbi, entry)); *offset =3D bytes & (sb->s_blocksize - 1); *blocknr =3D sbi->fat_start + (bytes >> sb->s_blocksize_bits); } @@ -353,7 +353,7 @@ int fat_ent_read(struct inode *inode, struct fat_entry = *fatent, int entry) int err, offset; sector_t blocknr; =20 - if (entry < FAT_START_ENT || sbi->max_cluster <=3D entry) { + if (!fat_valid_entry(sbi, entry)) { fatent_brelse(fatent); fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry); return -EIO; --=20 2.17.1