]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: skip the rmap and refcount btree checks when the levels are garbage
authorDarrick J. Wong <darrick.wong@oracle.com>
Fri, 20 Nov 2020 22:03:29 +0000 (17:03 -0500)
committerEric Sandeen <sandeen@sandeen.net>
Fri, 20 Nov 2020 22:03:29 +0000 (17:03 -0500)
In validate_ag[fi], we should check that the levels of the rmap and
refcount btrees are valid.  If they aren't, we need to tell phase4 to
skip the comparison between the existing and incore rmap and refcount
data.  The comparison routines use libxfs btree cursors, which assume
that the caller validated bc_nlevels and will corrupt memory if we load
a btree cursor with a garbage level count.

This was found by examing a core dump from a failed xfs/086 invocation.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
repair/scan.c

index a1d1ec941a8811b25c25916fcab28b5cf7e1cc10..44e794a0ac5a874e23d02833b7b7a63dd6c1b58d 100644 (file)
@@ -2288,23 +2288,31 @@ validate_agf(
 
        if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
                struct rmap_priv        priv;
+               unsigned int            levels;
 
                memset(&priv.high_key, 0xFF, sizeof(priv.high_key));
                priv.high_key.rm_blockcount = 0;
                priv.agcnts = agcnts;
                priv.last_rec.rm_owner = XFS_RMAP_OWN_UNKNOWN;
                priv.nr_blocks = 0;
+
+               levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]);
+               if (levels >= XFS_BTREE_MAXLEVELS) {
+                       do_warn(_("bad levels %u for rmapbt root, agno %d\n"),
+                               levels, agno);
+                       rmap_avoid_check();
+               }
+
                bno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]);
                if (libxfs_verify_agbno(mp, agno, bno)) {
-                       scan_sbtree(bno,
-                                   be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]),
-                                   agno, 0, scan_rmapbt, 1, XFS_RMAP_CRC_MAGIC,
-                                   &priv, &xfs_rmapbt_buf_ops);
+                       scan_sbtree(bno, levels, agno, 0, scan_rmapbt, 1,
+                                       XFS_RMAP_CRC_MAGIC, &priv,
+                                       &xfs_rmapbt_buf_ops);
                        if (be32_to_cpu(agf->agf_rmap_blocks) != priv.nr_blocks)
                                do_warn(_("bad rmapbt block count %u, saw %u\n"),
                                        priv.nr_blocks,
                                        be32_to_cpu(agf->agf_rmap_blocks));
-               } else  {
+               } else {
                        do_warn(_("bad agbno %u for rmapbt root, agno %d\n"),
                                bno, agno);
                        rmap_avoid_check();
@@ -2312,20 +2320,28 @@ validate_agf(
        }
 
        if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+               unsigned int    levels;
+
+               levels = be32_to_cpu(agf->agf_refcount_level);
+               if (levels >= XFS_BTREE_MAXLEVELS) {
+                       do_warn(_("bad levels %u for refcountbt root, agno %d\n"),
+                               levels, agno);
+                       refcount_avoid_check();
+               }
+
                bno = be32_to_cpu(agf->agf_refcount_root);
                if (libxfs_verify_agbno(mp, agno, bno)) {
                        struct refc_priv        priv;
 
                        memset(&priv, 0, sizeof(priv));
-                       scan_sbtree(bno,
-                                   be32_to_cpu(agf->agf_refcount_level),
-                                   agno, 0, scan_refcbt, 1, XFS_REFC_CRC_MAGIC,
-                                   &priv, &xfs_refcountbt_buf_ops);
+                       scan_sbtree(bno, levels, agno, 0, scan_refcbt, 1,
+                                       XFS_REFC_CRC_MAGIC, &priv,
+                                       &xfs_refcountbt_buf_ops);
                        if (be32_to_cpu(agf->agf_refcount_blocks) != priv.nr_blocks)
                                do_warn(_("bad refcountbt block count %u, saw %u\n"),
                                        priv.nr_blocks,
                                        be32_to_cpu(agf->agf_refcount_blocks));
-               } else  {
+               } else {
                        do_warn(_("bad agbno %u for refcntbt root, agno %d\n"),
                                bno, agno);
                        refcount_avoid_check();