From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Darren Kenny Date: Fri, 2 Jun 2023 18:08:44 +0000 Subject: [PATCH] fs/xfs: Fix issues found while fuzzing the XFS filesystem While performing fuzz testing with XFS filesystem images with ASAN enabled, several issues were found where the memory accesses are made beyond the data that is allocated into the struct grub_xfs_data structure's data field. The existing structure didn't store the size of the memory allocated into the buffer in the data field and had no way to check it. To resolve these issues, the data size is stored to enable checks into the data buffer. With these checks in place, the fuzzing corpus no longer cause any crashes. Signed-off-by: Darren Kenny Signed-off-by: Robbie Harwood Signed-off-by: Marta Lewandowska Signed-off-by: Lidong Chen Reviewed-by: Daniel Kiper --- grub-core/fs/xfs.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index b67407690c1a..b91cd32b49ab 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -239,6 +239,7 @@ struct grub_fshelp_node struct grub_xfs_data { + grub_size_t data_size; struct grub_xfs_sblock sblock; grub_disk_t disk; int pos; @@ -611,8 +612,20 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) } else if (node->inode.format == XFS_INODE_FORMAT_EXT) { + grub_addr_t exts_end = 0; + grub_addr_t data_end = 0; + nrec = grub_be_to_cpu32 (node->inode.nextents); exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode); + + if (grub_mul (sizeof (struct grub_xfs_extent), nrec, &exts_end) || + grub_add ((grub_addr_t) node->data, exts_end, &exts_end) || + grub_add ((grub_addr_t) node->data, node->data->data_size, &data_end) || + exts_end > data_end) + { + grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS extents"); + return 0; + } } else { @@ -803,6 +816,9 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de); grub_uint8_t c; + if ((inopos + (smallino ? 4 : 8)) > (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) + return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode"); + /* inopos might be unaligned. */ if (smallino) ino = (((grub_uint32_t) inopos[0]) << 24) @@ -829,6 +845,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, de->name[de->len] = c; de = grub_xfs_inline_next_de(dir->data, head, de); + + if ((grub_uint8_t *) de >= (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) + return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + } break; } @@ -897,6 +917,9 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, } filename = (char *)(direntry + 1); + if (filename + direntry->len - 1 > (char *) tail) + return grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + /* The byte after the filename is for the filetype, padding, or tag, which is not used by GRUB. So it can be overwritten. */ filename[direntry->len] = '\0'; @@ -941,6 +964,8 @@ grub_xfs_mount (grub_disk_t disk) if (!data) return 0; + data->data_size = sizeof (struct grub_xfs_data); + grub_dprintf("xfs", "Reading sb\n"); /* Read the superblock. */ if (grub_disk_read (disk, 0, 0, @@ -962,6 +987,7 @@ grub_xfs_mount (grub_disk_t disk) if (! data) goto fail; + data->data_size = sz; data->diropen.data = data; data->diropen.ino = grub_be_to_cpu64(data->sblock.rootino); data->diropen.inode_read = 1;