diff mbox series

xfs: add agf freeblocks verify in xfs_agf_verify

Message ID 1581587639-130771-1-git-send-email-zhengbin13@huawei.com (mailing list archive)
State Superseded
Headers show
Series xfs: add agf freeblocks verify in xfs_agf_verify | expand

Commit Message

Zheng Bin Feb. 13, 2020, 9:53 a.m. UTC
We recently used fuzz(hydra) to test XFS and automatically generate
tmp.img(XFS v5 format, but some metadata is wrong)

Test as follows:
mount tmp.img tmpdir
cp file1M tmpdir
sync

tmpdir/file1M size is 1M, but its data can not sync to disk.

This is because tmp.img has some problems, using xfs_repair detect
information as follows:

agf_freeblks 0, counted 3224 in ag 0
agf_longest 536874136, counted 3224 in ag 0
sb_fdblocks 613, counted 3228

Add these agf freeblocks checks:
1. agf_longest < agf_freeblks
2. agf_freeblks < sb_fdblocks

Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
Signed-off-by: Ren Xudong <renxudong1@huawei.com>
---
 fs/xfs/libxfs/xfs_alloc.c | 4 ++++
 1 file changed, 4 insertions(+)

--
2.7.4

Comments

Eric Sandeen Feb. 13, 2020, 1:37 p.m. UTC | #1
On 2/13/20 3:53 AM, Zheng Bin wrote:
> We recently used fuzz(hydra) to test XFS and automatically generate
> tmp.img(XFS v5 format, but some metadata is wrong)
> 
> Test as follows:
> mount tmp.img tmpdir
> cp file1M tmpdir
> sync

I would leave this part out of the change log, because it is not a
useful testcase for anyone who does not have your test.img.

> tmpdir/file1M size is 1M, but its data can not sync to disk.
> 
> This is because tmp.img has some problems, using xfs_repair detect
> information as follows:
> 
> agf_freeblks 0, counted 3224 in ag 0
> agf_longest 536874136, counted 3224 in ag 0
> sb_fdblocks 613, counted 3228
> 
> Add these agf freeblocks checks:
> 1. agf_longest < agf_freeblks
> 2. agf_freeblks < sb_fdblocks

This left out the 2nd check Dave suggested, that freeblocks is
less than the number of blocks in the AG, i.e.

b) agf_freeblks < sb_dblocks / sb_agcount

(and yes, it must special-case the last AG which may be smaller - see
xfs_ag_block_count())

-Eric


> Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
> Signed-off-by: Ren Xudong <renxudong1@huawei.com>
> ---
>  fs/xfs/libxfs/xfs_alloc.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
> index d8053bc..0f4b4d1 100644
> --- a/fs/xfs/libxfs/xfs_alloc.c
> +++ b/fs/xfs/libxfs/xfs_alloc.c
> @@ -2858,6 +2858,10 @@ xfs_agf_verify(
>  	      be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp)))
>  		return __this_address;
> 
> +	if (be32_to_cpu(agf->agf_freeblks) < be32_to_cpu(agf->agf_longest) ||
> +	    be32_to_cpu(agf->agf_freeblks) >= mp->m_sb.sb_fdblocks)
> +		return __this_address;
> +
>  	if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) < 1 ||
>  	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) < 1 ||
>  	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
> --
> 2.7.4
>
Darrick J. Wong Feb. 13, 2020, 5:44 p.m. UTC | #2
On Thu, Feb 13, 2020 at 05:53:59PM +0800, Zheng Bin wrote:
> We recently used fuzz(hydra) to test XFS and automatically generate
> tmp.img(XFS v5 format, but some metadata is wrong)
> 
> Test as follows:
> mount tmp.img tmpdir
> cp file1M tmpdir
> sync
> 
> tmpdir/file1M size is 1M, but its data can not sync to disk.
> 
> This is because tmp.img has some problems, using xfs_repair detect
> information as follows:
> 
> agf_freeblks 0, counted 3224 in ag 0
> agf_longest 536874136, counted 3224 in ag 0
> sb_fdblocks 613, counted 3228
> 
> Add these agf freeblocks checks:
> 1. agf_longest < agf_freeblks
> 2. agf_freeblks < sb_fdblocks

Uh... what problem did you encounter?  Did block allocation loop
forever?  Did errors come pouring out of dmesg?  Did other strange
behaviors erupt?  What is the smallest number of steps needed to go from
a fresh format to ... whatever went wrong here?

That's what this commit message ought to capture. :)

--D

> Signed-off-by: Zheng Bin <zhengbin13@huawei.com>
> Signed-off-by: Ren Xudong <renxudong1@huawei.com>
> ---
>  fs/xfs/libxfs/xfs_alloc.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
> index d8053bc..0f4b4d1 100644
> --- a/fs/xfs/libxfs/xfs_alloc.c
> +++ b/fs/xfs/libxfs/xfs_alloc.c
> @@ -2858,6 +2858,10 @@ xfs_agf_verify(
>  	      be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp)))
>  		return __this_address;
> 
> +	if (be32_to_cpu(agf->agf_freeblks) < be32_to_cpu(agf->agf_longest) ||
> +	    be32_to_cpu(agf->agf_freeblks) >= mp->m_sb.sb_fdblocks)
> +		return __this_address;
> +
>  	if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) < 1 ||
>  	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) < 1 ||
>  	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||
> --
> 2.7.4
>
Dave Chinner Feb. 13, 2020, 8:55 p.m. UTC | #3
On Thu, Feb 13, 2020 at 05:53:59PM +0800, Zheng Bin wrote:
> We recently used fuzz(hydra) to test XFS and automatically generate
> tmp.img(XFS v5 format, but some metadata is wrong)
> 
> Test as follows:
> mount tmp.img tmpdir
> cp file1M tmpdir
> sync
> 
> tmpdir/file1M size is 1M, but its data can not sync to disk.
> 
> This is because tmp.img has some problems, using xfs_repair detect
> information as follows:
> 
> agf_freeblks 0, counted 3224 in ag 0
> agf_longest 536874136, counted 3224 in ag 0
> sb_fdblocks 613, counted 3228
> 
> Add these agf freeblocks checks:
> 1. agf_longest < agf_freeblks
> 2. agf_freeblks < sb_fdblocks

Did you audit the other fields in the AGF to see if they were
adequately bounds checked by xfs_agf_verify()?

A quick look at struct xfs_agf and xfs_agf_verify() indicates that
agf_length, agf_rmap_blocks and agf_refcount_blocks are not bounds
checked, either. And agf_spare64 and agf_spare2 are not checked for
being zero....

Cheers,

Dave.
diff mbox series

Patch

diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index d8053bc..0f4b4d1 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -2858,6 +2858,10 @@  xfs_agf_verify(
 	      be32_to_cpu(agf->agf_flcount) <= xfs_agfl_size(mp)))
 		return __this_address;

+	if (be32_to_cpu(agf->agf_freeblks) < be32_to_cpu(agf->agf_longest) ||
+	    be32_to_cpu(agf->agf_freeblks) >= mp->m_sb.sb_fdblocks)
+		return __this_address;
+
 	if (be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) < 1 ||
 	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]) < 1 ||
 	    be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]) > XFS_BTREE_MAXLEVELS ||