There is a bit of an inconsistency in how ->b_maps is used for
contiguous buffers between kernel libxfs and xfsprogs due to the
independent buffer implementations. In the kernel, ->b_maps[0] is
always intialized to a valid range and in xfsprogs, ->b_maps is only
allocated for discontiguous buffers.
This can lead to confusion when dealing with uncached kernel buffers
in common libxfs code because xfsprogs has no concept of uncached
buffers. Kernel uncached buffers have ->b_bn == XFS_BUF_DADDR_NULL
and ->b_maps[0] points to the physical block address. Block address
checks in common code for kernel uncached buffers, such as in
xfs_sb_verify(), therefore would need to check both places for an
address or risk broken logic or userspace segfaults.
This problem currently manifests as an xfs_repair segfault due to a
NULL ->b_maps access in xfs_sb_verify(). Note that this problem is
only reproducible on builds with (-O2) optimization disabled, as the
affected parameter is currently unused and thus optimization
eliminates the problematic access.
To fix this problem and eliminate the incompatibility, update the
userspace xfs_buf with an internal ->__b_map field and point
->b_maps to it for contiguous buffers, similar to the kernel buffer
implementation. Set valid values in ->b_maps0] for contiguous
buffers so common code will continue to work regardless of whether a
buffer is uncached in the kernel.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Acked-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
const struct xfs_buf_ops *b_ops;
struct xfs_perag *b_pag;
struct xfs_buf_map *b_maps;
+ struct xfs_buf_map __b_map;
int b_nmaps;
#ifdef XFS_BUF_TRACING
struct list_head b_lock_list;
bp->b_holder = 0;
bp->b_recur = 0;
bp->b_ops = NULL;
+
+ if (!bp->b_maps) {
+ bp->b_nmaps = 1;
+ bp->b_maps = &bp->__b_map;
+ bp->b_maps[0].bm_bn = bp->b_bn;
+ bp->b_maps[0].bm_len = bp->b_length;
+ }
}
static void
list_del_init(&bp->b_node.cn_mru);
free(bp->b_addr);
bp->b_addr = NULL;
- free(bp->b_maps);
+ if (bp->b_maps != &bp->__b_map)
+ free(bp->b_maps);
bp->b_maps = NULL;
}
} else