Skip to content

Commit 2d7d1e7

Browse files
author
Darrick J. Wong
committed
xfs: AGI length should be bounds checked
Similar to the recent patch strengthening the AGF agf_length verification, the AGI verifier does not check that the AGI length field is within known good bounds. This isn't currently checked by runtime kernel code, yet we assume in many places that it is correct and verify other metadata against it. Add length verification to the AGI verifier. Just like the AGF length checking, the length of the AGI must be equal to the size of the AG specified in the superblock, unless it is the last AG in the filesystem. In that case, it must be less than or equal to sb->sb_agblocks and greater than XFS_MIN_AG_BLOCKS, which is the smallest AG a growfs operation will allow to exist. There's only one place in the filesystem that actually uses agi_length, but let's not leave it vulnerable to the same weird nonsense that generates syzbot bugs, eh? Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent 5cf32f6 commit 2d7d1e7

File tree

3 files changed

+60
-39
lines changed

3 files changed

+60
-39
lines changed

fs/xfs/libxfs/xfs_alloc.c

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2956,6 +2956,47 @@ xfs_alloc_put_freelist(
29562956
return 0;
29572957
}
29582958

2959+
/*
2960+
* Check that this AGF/AGI header's sequence number and length matches the AG
2961+
* number and size in fsblocks.
2962+
*/
2963+
xfs_failaddr_t
2964+
xfs_validate_ag_length(
2965+
struct xfs_buf *bp,
2966+
uint32_t seqno,
2967+
uint32_t length)
2968+
{
2969+
struct xfs_mount *mp = bp->b_mount;
2970+
/*
2971+
* During growfs operations, the perag is not fully initialised,
2972+
* so we can't use it for any useful checking. growfs ensures we can't
2973+
* use it by using uncached buffers that don't have the perag attached
2974+
* so we can detect and avoid this problem.
2975+
*/
2976+
if (bp->b_pag && seqno != bp->b_pag->pag_agno)
2977+
return __this_address;
2978+
2979+
/*
2980+
* Only the last AG in the filesystem is allowed to be shorter
2981+
* than the AG size recorded in the superblock.
2982+
*/
2983+
if (length != mp->m_sb.sb_agblocks) {
2984+
/*
2985+
* During growfs, the new last AG can get here before we
2986+
* have updated the superblock. Give it a pass on the seqno
2987+
* check.
2988+
*/
2989+
if (bp->b_pag && seqno != mp->m_sb.sb_agcount - 1)
2990+
return __this_address;
2991+
if (length < XFS_MIN_AG_BLOCKS)
2992+
return __this_address;
2993+
if (length > mp->m_sb.sb_agblocks)
2994+
return __this_address;
2995+
}
2996+
2997+
return NULL;
2998+
}
2999+
29593000
/*
29603001
* Verify the AGF is consistent.
29613002
*
@@ -2975,6 +3016,8 @@ xfs_agf_verify(
29753016
{
29763017
struct xfs_mount *mp = bp->b_mount;
29773018
struct xfs_agf *agf = bp->b_addr;
3019+
xfs_failaddr_t fa;
3020+
uint32_t agf_seqno = be32_to_cpu(agf->agf_seqno);
29783021
uint32_t agf_length = be32_to_cpu(agf->agf_length);
29793022

29803023
if (xfs_has_crc(mp)) {
@@ -2993,33 +3036,10 @@ xfs_agf_verify(
29933036
/*
29943037
* Both agf_seqno and agf_length need to validated before anything else
29953038
* block number related in the AGF or AGFL can be checked.
2996-
*
2997-
* During growfs operations, the perag is not fully initialised,
2998-
* so we can't use it for any useful checking. growfs ensures we can't
2999-
* use it by using uncached buffers that don't have the perag attached
3000-
* so we can detect and avoid this problem.
3001-
*/
3002-
if (bp->b_pag && be32_to_cpu(agf->agf_seqno) != bp->b_pag->pag_agno)
3003-
return __this_address;
3004-
3005-
/*
3006-
* Only the last AGF in the filesytsem is allowed to be shorter
3007-
* than the AG size recorded in the superblock.
30083039
*/
3009-
if (agf_length != mp->m_sb.sb_agblocks) {
3010-
/*
3011-
* During growfs, the new last AGF can get here before we
3012-
* have updated the superblock. Give it a pass on the seqno
3013-
* check.
3014-
*/
3015-
if (bp->b_pag &&
3016-
be32_to_cpu(agf->agf_seqno) != mp->m_sb.sb_agcount - 1)
3017-
return __this_address;
3018-
if (agf_length < XFS_MIN_AG_BLOCKS)
3019-
return __this_address;
3020-
if (agf_length > mp->m_sb.sb_agblocks)
3021-
return __this_address;
3022-
}
3040+
fa = xfs_validate_ag_length(bp, agf_seqno, agf_length);
3041+
if (fa)
3042+
return fa;
30233043

30243044
if (be32_to_cpu(agf->agf_flfirst) >= xfs_agfl_size(mp))
30253045
return __this_address;

fs/xfs/libxfs/xfs_alloc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,7 @@ extern struct kmem_cache *xfs_extfree_item_cache;
273273
int __init xfs_extfree_intent_init_cache(void);
274274
void xfs_extfree_intent_destroy_cache(void);
275275

276+
xfs_failaddr_t xfs_validate_ag_length(struct xfs_buf *bp, uint32_t seqno,
277+
uint32_t length);
278+
276279
#endif /* __XFS_ALLOC_H__ */

fs/xfs/libxfs/xfs_ialloc.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,11 +2486,14 @@ xfs_ialloc_log_agi(
24862486

24872487
static xfs_failaddr_t
24882488
xfs_agi_verify(
2489-
struct xfs_buf *bp)
2489+
struct xfs_buf *bp)
24902490
{
2491-
struct xfs_mount *mp = bp->b_mount;
2492-
struct xfs_agi *agi = bp->b_addr;
2493-
int i;
2491+
struct xfs_mount *mp = bp->b_mount;
2492+
struct xfs_agi *agi = bp->b_addr;
2493+
xfs_failaddr_t fa;
2494+
uint32_t agi_seqno = be32_to_cpu(agi->agi_seqno);
2495+
uint32_t agi_length = be32_to_cpu(agi->agi_length);
2496+
int i;
24942497

24952498
if (xfs_has_crc(mp)) {
24962499
if (!uuid_equal(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid))
@@ -2507,6 +2510,10 @@ xfs_agi_verify(
25072510
if (!XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)))
25082511
return __this_address;
25092512

2513+
fa = xfs_validate_ag_length(bp, agi_seqno, agi_length);
2514+
if (fa)
2515+
return fa;
2516+
25102517
if (be32_to_cpu(agi->agi_level) < 1 ||
25112518
be32_to_cpu(agi->agi_level) > M_IGEO(mp)->inobt_maxlevels)
25122519
return __this_address;
@@ -2516,15 +2523,6 @@ xfs_agi_verify(
25162523
be32_to_cpu(agi->agi_free_level) > M_IGEO(mp)->inobt_maxlevels))
25172524
return __this_address;
25182525

2519-
/*
2520-
* during growfs operations, the perag is not fully initialised,
2521-
* so we can't use it for any useful checking. growfs ensures we can't
2522-
* use it by using uncached buffers that don't have the perag attached
2523-
* so we can detect and avoid this problem.
2524-
*/
2525-
if (bp->b_pag && be32_to_cpu(agi->agi_seqno) != bp->b_pag->pag_agno)
2526-
return __this_address;
2527-
25282526
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
25292527
if (agi->agi_unlinked[i] == cpu_to_be32(NULLAGINO))
25302528
continue;

0 commit comments

Comments
 (0)