diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c index dbdb21d156..b5c53d3d22 100644 --- a/tools/libfsimage/xfs/fsys_xfs.c +++ b/tools/libfsimage/xfs/fsys_xfs.c @@ -18,6 +18,7 @@ */ #include +#include #include #include "xfs.h" @@ -431,29 +432,56 @@ first_dentry (fsi_file_t *ffi, xfs_ino_t *ino) return next_dentry (ffi, ino); } +static bool +xfs_sb_is_invalid (const xfs_sb_t *super) +{ + return (le32(super->sb_magicnum) != XFS_SB_MAGIC) + || ((le16(super->sb_versionnum) & XFS_SB_VERSION_NUMBITS) != + XFS_SB_VERSION_4) + || (super->sb_inodelog < XFS_SB_INODELOG_MIN) + || (super->sb_inodelog > XFS_SB_INODELOG_MAX) + || (super->sb_blocklog < XFS_SB_BLOCKLOG_MIN) + || (super->sb_blocklog > XFS_SB_BLOCKLOG_MAX) + || (super->sb_blocklog < super->sb_inodelog) + || (super->sb_agblklog > XFS_SB_AGBLKLOG_MAX) + || ((1ull << super->sb_agblklog) < le32(super->sb_agblocks)) + || (((1ull << super->sb_agblklog) >> 1) >= + le32(super->sb_agblocks)) + || ((super->sb_blocklog + super->sb_dirblklog) >= + XFS_SB_DIRBLK_NUMBITS); +} + static int xfs_mount (fsi_file_t *ffi, const char *options) { xfs_sb_t super; if (!devread (ffi, 0, 0, sizeof(super), (char *)&super) - || (le32(super.sb_magicnum) != XFS_SB_MAGIC) - || ((le16(super.sb_versionnum) - & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { + || xfs_sb_is_invalid(&super)) { return 0; } - xfs.bsize = le32 (super.sb_blocksize); - xfs.blklog = super.sb_blocklog; - xfs.bdlog = xfs.blklog - SECTOR_BITS; + /* + * Not sanitized. It's exclusively used to generate disk addresses, + * so it's not important from a security standpoint. + */ xfs.rootino = le64 (super.sb_rootino); - xfs.isize = le16 (super.sb_inodesize); - xfs.agblocks = le32 (super.sb_agblocks); - xfs.dirbsize = xfs.bsize << super.sb_dirblklog; - xfs.inopblog = super.sb_inopblog; + /* + * Sanitized to be consistent with each other, only used to + * generate disk addresses, so it's safe + */ + xfs.agblocks = le32 (super.sb_agblocks); xfs.agblklog = super.sb_agblklog; + /* Derived from sanitized parameters */ + xfs.bsize = 1 << super.sb_blocklog; + xfs.blklog = super.sb_blocklog; + xfs.bdlog = super.sb_blocklog - SECTOR_BITS; + xfs.isize = 1 << super.sb_inodelog; + xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog); + xfs.inopblog = super.sb_blocklog - super.sb_inodelog; + xfs.btnode_ptr0_off = ((xfs.bsize - sizeof(xfs_btree_block_t)) / (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) diff --git a/tools/libfsimage/xfs/xfs.h b/tools/libfsimage/xfs/xfs.h index 40699281e4..b87e37d3d7 100644 --- a/tools/libfsimage/xfs/xfs.h +++ b/tools/libfsimage/xfs/xfs.h @@ -134,6 +134,18 @@ typedef struct xfs_sb xfs_uint8_t sb_dummy[7]; /* padding */ } xfs_sb_t; +/* Bound taken from xfs.c in GRUB2. It doesn't exist in the spec */ +#define XFS_SB_DIRBLK_NUMBITS 27 +/* Implied by the XFS specification. The minimum block size is 512 octets */ +#define XFS_SB_BLOCKLOG_MIN 9 +/* Implied by the XFS specification. The maximum block size is 65536 octets */ +#define XFS_SB_BLOCKLOG_MAX 16 +/* Implied by the XFS specification. The minimum inode size is 256 octets */ +#define XFS_SB_INODELOG_MIN 8 +/* Implied by the XFS specification. The maximum inode size is 2048 octets */ +#define XFS_SB_INODELOG_MAX 11 +/* High bound for sb_agblklog */ +#define XFS_SB_AGBLKLOG_MAX 32 /* those are from xfs_btree.h */