]> git-server-git.apps.pok.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs: strengthen attr leaf block freemap checking
authorDarrick J. Wong <djwong@kernel.org>
Sun, 22 Feb 2026 22:41:09 +0000 (14:41 -0800)
committerAndrey Albershteyn <aalbersh@kernel.org>
Wed, 8 Apr 2026 19:39:56 +0000 (21:39 +0200)
Source kernel commit: 27a0c41f33d8d31558d334b07eb58701aab0b3dd

Check for erroneous overlapping freemap regions and collisions between
freemap regions and the xattr leaf entry array.

Note that we must explicitly zero out the extra freemaps in
xfs_attr3_leaf_compact so that the in-memory buffer has a correctly
initialized freemap array to satisfy the new verification code, even if
subsequent code changes the contents before unlocking the buffer.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
libxfs/xfs_attr_leaf.c

index 158864249c8888c0d55b25b751e2fa4e68b5dfaf..f591984c3748ff8d27e12ebf7e293db4867bf8b3 100644 (file)
@@ -82,6 +82,49 @@ xfs_attr_leaf_entries_end(
                        xfs_attr3_leaf_hdr_size(leaf);
 }
 
+static inline bool
+ichdr_freemaps_overlap(
+       const struct xfs_attr3_icleaf_hdr       *ichdr,
+       unsigned int                            x,
+       unsigned int                            y)
+{
+       const unsigned int                      xend =
+               ichdr->freemap[x].base + ichdr->freemap[x].size;
+       const unsigned int                      yend =
+               ichdr->freemap[y].base + ichdr->freemap[y].size;
+
+       /* empty slots do not overlap */
+       if (!ichdr->freemap[x].size || !ichdr->freemap[y].size)
+               return false;
+
+       return ichdr->freemap[x].base < yend && xend > ichdr->freemap[y].base;
+}
+
+static inline xfs_failaddr_t
+xfs_attr_leaf_ichdr_freemaps_verify(
+       const struct xfs_attr3_icleaf_hdr       *ichdr,
+       const struct xfs_attr_leafblock         *leaf)
+{
+       unsigned int                            entries_end =
+               xfs_attr_leaf_entries_end(ichdr->count, leaf);
+       int                                     i;
+
+       if (ichdr_freemaps_overlap(ichdr, 0, 1))
+               return __this_address;
+       if (ichdr_freemaps_overlap(ichdr, 0, 2))
+               return __this_address;
+       if (ichdr_freemaps_overlap(ichdr, 1, 2))
+               return __this_address;
+
+       for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; i++) {
+               if (ichdr->freemap[i].size > 0 &&
+                   ichdr->freemap[i].base < entries_end)
+                       return __this_address;
+       }
+
+       return NULL;
+}
+
 /*
  * attr3 block 'firstused' conversion helpers.
  *
@@ -225,6 +268,8 @@ xfs_attr3_leaf_hdr_to_disk(
                        hdr3->freemap[i].base = cpu_to_be16(from->freemap[i].base);
                        hdr3->freemap[i].size = cpu_to_be16(from->freemap[i].size);
                }
+
+               ASSERT(xfs_attr_leaf_ichdr_freemaps_verify(from, to) == NULL);
                return;
        }
        to->hdr.info.forw = cpu_to_be32(from->forw);
@@ -240,6 +285,8 @@ xfs_attr3_leaf_hdr_to_disk(
                to->hdr.freemap[i].base = cpu_to_be16(from->freemap[i].base);
                to->hdr.freemap[i].size = cpu_to_be16(from->freemap[i].size);
        }
+
+       ASSERT(xfs_attr_leaf_ichdr_freemaps_verify(from, to) == NULL);
 }
 
 static xfs_failaddr_t
@@ -392,6 +439,10 @@ xfs_attr3_leaf_verify(
                        return __this_address;
        }
 
+       fa = xfs_attr_leaf_ichdr_freemaps_verify(&ichdr, leaf);
+       if (fa)
+               return fa;
+
        return NULL;
 }
 
@@ -1661,6 +1712,10 @@ xfs_attr3_leaf_compact(
        ichdr_dst->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_src);
        ichdr_dst->freemap[0].size = ichdr_dst->firstused -
                                                ichdr_dst->freemap[0].base;
+       ichdr_dst->freemap[1].base = 0;
+       ichdr_dst->freemap[2].base = 0;
+       ichdr_dst->freemap[1].size = 0;
+       ichdr_dst->freemap[2].size = 0;
 
        /* write the header back to initialise the underlying buffer */
        xfs_attr3_leaf_hdr_to_disk(args->geo, leaf_dst, ichdr_dst);