Skip to content

Commit dc04db2

Browse files
Dave Chinnerdchinner
authored andcommitted
xfs: detect self referencing btree sibling pointers
To catch the obvious graph cycle problem and hence potential endless looping. Signed-off-by: Dave Chinner <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Dave Chinner <[email protected]>
1 parent a44a027 commit dc04db2

File tree

1 file changed

+105
-35
lines changed

1 file changed

+105
-35
lines changed

fs/xfs/libxfs/xfs_btree.c

Lines changed: 105 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,52 @@ xfs_btree_magic(
5151
return magic;
5252
}
5353

54+
static xfs_failaddr_t
55+
xfs_btree_check_lblock_siblings(
56+
struct xfs_mount *mp,
57+
struct xfs_btree_cur *cur,
58+
int level,
59+
xfs_fsblock_t fsb,
60+
xfs_fsblock_t sibling)
61+
{
62+
if (sibling == NULLFSBLOCK)
63+
return NULL;
64+
if (sibling == fsb)
65+
return __this_address;
66+
if (level >= 0) {
67+
if (!xfs_btree_check_lptr(cur, sibling, level + 1))
68+
return __this_address;
69+
} else {
70+
if (!xfs_verify_fsbno(mp, sibling))
71+
return __this_address;
72+
}
73+
74+
return NULL;
75+
}
76+
77+
static xfs_failaddr_t
78+
xfs_btree_check_sblock_siblings(
79+
struct xfs_mount *mp,
80+
struct xfs_btree_cur *cur,
81+
int level,
82+
xfs_agnumber_t agno,
83+
xfs_agblock_t agbno,
84+
xfs_agblock_t sibling)
85+
{
86+
if (sibling == NULLAGBLOCK)
87+
return NULL;
88+
if (sibling == agbno)
89+
return __this_address;
90+
if (level >= 0) {
91+
if (!xfs_btree_check_sptr(cur, sibling, level + 1))
92+
return __this_address;
93+
} else {
94+
if (!xfs_verify_agbno(mp, agno, sibling))
95+
return __this_address;
96+
}
97+
return NULL;
98+
}
99+
54100
/*
55101
* Check a long btree block header. Return the address of the failing check,
56102
* or NULL if everything is ok.
@@ -65,6 +111,8 @@ __xfs_btree_check_lblock(
65111
struct xfs_mount *mp = cur->bc_mp;
66112
xfs_btnum_t btnum = cur->bc_btnum;
67113
int crc = xfs_has_crc(mp);
114+
xfs_failaddr_t fa;
115+
xfs_fsblock_t fsb = NULLFSBLOCK;
68116

69117
if (crc) {
70118
if (!uuid_equal(&block->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid))
@@ -83,16 +131,16 @@ __xfs_btree_check_lblock(
83131
if (be16_to_cpu(block->bb_numrecs) >
84132
cur->bc_ops->get_maxrecs(cur, level))
85133
return __this_address;
86-
if (block->bb_u.l.bb_leftsib != cpu_to_be64(NULLFSBLOCK) &&
87-
!xfs_btree_check_lptr(cur, be64_to_cpu(block->bb_u.l.bb_leftsib),
88-
level + 1))
89-
return __this_address;
90-
if (block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK) &&
91-
!xfs_btree_check_lptr(cur, be64_to_cpu(block->bb_u.l.bb_rightsib),
92-
level + 1))
93-
return __this_address;
94134

95-
return NULL;
135+
if (bp)
136+
fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
137+
138+
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb,
139+
be64_to_cpu(block->bb_u.l.bb_leftsib));
140+
if (!fa)
141+
fa = xfs_btree_check_lblock_siblings(mp, cur, level, fsb,
142+
be64_to_cpu(block->bb_u.l.bb_rightsib));
143+
return fa;
96144
}
97145

98146
/* Check a long btree block header. */
@@ -130,6 +178,9 @@ __xfs_btree_check_sblock(
130178
struct xfs_mount *mp = cur->bc_mp;
131179
xfs_btnum_t btnum = cur->bc_btnum;
132180
int crc = xfs_has_crc(mp);
181+
xfs_failaddr_t fa;
182+
xfs_agblock_t agbno = NULLAGBLOCK;
183+
xfs_agnumber_t agno = NULLAGNUMBER;
133184

134185
if (crc) {
135186
if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
@@ -146,16 +197,18 @@ __xfs_btree_check_sblock(
146197
if (be16_to_cpu(block->bb_numrecs) >
147198
cur->bc_ops->get_maxrecs(cur, level))
148199
return __this_address;
149-
if (block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK) &&
150-
!xfs_btree_check_sptr(cur, be32_to_cpu(block->bb_u.s.bb_leftsib),
151-
level + 1))
152-
return __this_address;
153-
if (block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK) &&
154-
!xfs_btree_check_sptr(cur, be32_to_cpu(block->bb_u.s.bb_rightsib),
155-
level + 1))
156-
return __this_address;
157200

158-
return NULL;
201+
if (bp) {
202+
agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
203+
agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
204+
}
205+
206+
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, agbno,
207+
be32_to_cpu(block->bb_u.s.bb_leftsib));
208+
if (!fa)
209+
fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno,
210+
agbno, be32_to_cpu(block->bb_u.s.bb_rightsib));
211+
return fa;
159212
}
160213

161214
/* Check a short btree block header. */
@@ -4271,6 +4324,21 @@ xfs_btree_visit_block(
42714324
if (xfs_btree_ptr_is_null(cur, &rptr))
42724325
return -ENOENT;
42734326

4327+
/*
4328+
* We only visit blocks once in this walk, so we have to avoid the
4329+
* internal xfs_btree_lookup_get_block() optimisation where it will
4330+
* return the same block without checking if the right sibling points
4331+
* back to us and creates a cyclic reference in the btree.
4332+
*/
4333+
if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
4334+
if (be64_to_cpu(rptr.l) == XFS_DADDR_TO_FSB(cur->bc_mp,
4335+
xfs_buf_daddr(bp)))
4336+
return -EFSCORRUPTED;
4337+
} else {
4338+
if (be32_to_cpu(rptr.s) == xfs_daddr_to_agbno(cur->bc_mp,
4339+
xfs_buf_daddr(bp)))
4340+
return -EFSCORRUPTED;
4341+
}
42744342
return xfs_btree_lookup_get_block(cur, level, &rptr, &block);
42754343
}
42764344

@@ -4445,20 +4513,21 @@ xfs_btree_lblock_verify(
44454513
{
44464514
struct xfs_mount *mp = bp->b_mount;
44474515
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
4516+
xfs_fsblock_t fsb;
4517+
xfs_failaddr_t fa;
44484518

44494519
/* numrecs verification */
44504520
if (be16_to_cpu(block->bb_numrecs) > max_recs)
44514521
return __this_address;
44524522

44534523
/* sibling pointer verification */
4454-
if (block->bb_u.l.bb_leftsib != cpu_to_be64(NULLFSBLOCK) &&
4455-
!xfs_verify_fsbno(mp, be64_to_cpu(block->bb_u.l.bb_leftsib)))
4456-
return __this_address;
4457-
if (block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK) &&
4458-
!xfs_verify_fsbno(mp, be64_to_cpu(block->bb_u.l.bb_rightsib)))
4459-
return __this_address;
4460-
4461-
return NULL;
4524+
fsb = XFS_DADDR_TO_FSB(mp, xfs_buf_daddr(bp));
4525+
fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb,
4526+
be64_to_cpu(block->bb_u.l.bb_leftsib));
4527+
if (!fa)
4528+
fa = xfs_btree_check_lblock_siblings(mp, NULL, -1, fsb,
4529+
be64_to_cpu(block->bb_u.l.bb_rightsib));
4530+
return fa;
44624531
}
44634532

44644533
/**
@@ -4499,22 +4568,23 @@ xfs_btree_sblock_verify(
44994568
{
45004569
struct xfs_mount *mp = bp->b_mount;
45014570
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
4502-
xfs_agblock_t agno;
4571+
xfs_agnumber_t agno;
4572+
xfs_agblock_t agbno;
4573+
xfs_failaddr_t fa;
45034574

45044575
/* numrecs verification */
45054576
if (be16_to_cpu(block->bb_numrecs) > max_recs)
45064577
return __this_address;
45074578

45084579
/* sibling pointer verification */
45094580
agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
4510-
if (block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK) &&
4511-
!xfs_verify_agbno(mp, agno, be32_to_cpu(block->bb_u.s.bb_leftsib)))
4512-
return __this_address;
4513-
if (block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK) &&
4514-
!xfs_verify_agbno(mp, agno, be32_to_cpu(block->bb_u.s.bb_rightsib)))
4515-
return __this_address;
4516-
4517-
return NULL;
4581+
agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
4582+
fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
4583+
be32_to_cpu(block->bb_u.s.bb_leftsib));
4584+
if (!fa)
4585+
fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
4586+
be32_to_cpu(block->bb_u.s.bb_rightsib));
4587+
return fa;
45184588
}
45194589

45204590
/*

0 commit comments

Comments
 (0)