Blame SOURCES/0264-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch

1c6ba0
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
1c6ba0
From: Darren Kenny <darren.kenny@oracle.com>
1c6ba0
Date: Tue, 29 Mar 2022 15:52:46 +0000
1c6ba0
Subject: [PATCH] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing
1c6ba0
1c6ba0
The fuzzer is generating btrfs file systems that have chunks with
1c6ba0
invalid combinations of stripes and substripes for the given RAID
1c6ba0
configurations.
1c6ba0
1c6ba0
After examining the Linux kernel fs/btrfs/tree-checker.c code, it
1c6ba0
appears that sub-stripes should only be applied to RAID10, and in that
1c6ba0
case there should only ever be 2 of them.
1c6ba0
1c6ba0
Similarly, RAID single should only have 1 stripe, and RAID1/1C3/1C4
1c6ba0
should have 2. 3 or 4 stripes respectively, which is what redundancy
1c6ba0
corresponds.
1c6ba0
1c6ba0
Some of the chunks ended up with a size of 0, which grub_malloc() still
1c6ba0
returned memory for and in turn generated ASAN errors later when
1c6ba0
accessed.
1c6ba0
1c6ba0
While it would be possible to specifically limit the number of stripes,
1c6ba0
a more correct test was on the combination of the chunk item, and the
1c6ba0
number of stripes by the size of the chunk stripe structure in
1c6ba0
comparison to the size of the chunk itself.
1c6ba0
1c6ba0
Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
1c6ba0
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
1c6ba0
(cherry picked from commit 3849647b4b98a4419366708fc4b7f339c6f55ec7)
1c6ba0
(cherry picked from commit fa5a02a8930bbd8a3b5ae6ed9612307611f18500)
1c6ba0
---
1c6ba0
 grub-core/fs/btrfs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1c6ba0
 1 file changed, 55 insertions(+)
1c6ba0
1c6ba0
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
1c6ba0
index f3ab64e098..b104da085c 100644
1c6ba0
--- a/grub-core/fs/btrfs.c
1c6ba0
+++ b/grub-core/fs/btrfs.c
1c6ba0
@@ -941,6 +941,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
1c6ba0
 	return grub_error (GRUB_ERR_BAD_FS,
1c6ba0
 			   "couldn't find the chunk descriptor");
1c6ba0
 
1c6ba0
+      if (!chsize)
1c6ba0
+	{
1c6ba0
+	  grub_dprintf ("btrfs", "zero-size chunk\n");
1c6ba0
+	  return grub_error (GRUB_ERR_BAD_FS,
1c6ba0
+			     "got an invalid zero-size chunk");
1c6ba0
+	}
1c6ba0
       chunk = grub_malloc (chsize);
1c6ba0
       if (!chunk)
1c6ba0
 	return grub_errno;
1c6ba0
@@ -999,6 +1005,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
1c6ba0
 	      stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
1c6ba0
 					     nstripes,
1c6ba0
 					     NULL);
1c6ba0
+
1c6ba0
+	      /* For single, there should be exactly 1 stripe. */
1c6ba0
+	      if (grub_le_to_cpu16 (chunk->nstripes) != 1)
1c6ba0
+		{
1c6ba0
+		  grub_dprintf ("btrfs", "invalid RAID_SINGLE: nstripes != 1 (%u)\n",
1c6ba0
+				grub_le_to_cpu16 (chunk->nstripes));
1c6ba0
+		  return grub_error (GRUB_ERR_BAD_FS,
1c6ba0
+				     "invalid RAID_SINGLE: nstripes != 1 (%u)",
1c6ba0
+				      grub_le_to_cpu16 (chunk->nstripes));
1c6ba0
+		}
1c6ba0
 	      if (stripe_length == 0)
1c6ba0
 		stripe_length = 512;
1c6ba0
 	      stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
1c6ba0
@@ -1018,6 +1034,19 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
1c6ba0
 	      stripen = 0;
1c6ba0
 	      stripe_offset = off;
1c6ba0
 	      csize = grub_le_to_cpu64 (chunk->size) - off;
1c6ba0
+
1c6ba0
+             /*
1c6ba0
+	      * Redundancy, and substripes only apply to RAID10, and there
1c6ba0
+	      * should be exactly 2 sub-stripes.
1c6ba0
+	      */
1c6ba0
+	     if (grub_le_to_cpu16 (chunk->nstripes) != redundancy)
1c6ba0
+               {
1c6ba0
+                 grub_dprintf ("btrfs", "invalid RAID1: nstripes != %u (%u)\n",
1c6ba0
+                               redundancy, grub_le_to_cpu16 (chunk->nstripes));
1c6ba0
+                 return grub_error (GRUB_ERR_BAD_FS,
1c6ba0
+                                    "invalid RAID1: nstripes != %u (%u)",
1c6ba0
+                                    redundancy, grub_le_to_cpu16 (chunk->nstripes));
1c6ba0
+               }
1c6ba0
 	      break;
1c6ba0
 	    }
1c6ba0
 	  case GRUB_BTRFS_CHUNK_TYPE_RAID0:
1c6ba0
@@ -1054,6 +1083,20 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
1c6ba0
 	      stripe_offset = low + chunk_stripe_length
1c6ba0
 		* high;
1c6ba0
 	      csize = chunk_stripe_length - low;
1c6ba0
+
1c6ba0
+	      /*
1c6ba0
+	       * Substripes only apply to RAID10, and there
1c6ba0
+	       * should be exactly 2 sub-stripes.
1c6ba0
+	       */
1c6ba0
+	      if (grub_le_to_cpu16 (chunk->nsubstripes) != 2)
1c6ba0
+		{
1c6ba0
+		  grub_dprintf ("btrfs", "invalid RAID10: nsubstripes != 2 (%u)",
1c6ba0
+				grub_le_to_cpu16 (chunk->nsubstripes));
1c6ba0
+		  return grub_error (GRUB_ERR_BAD_FS,
1c6ba0
+				     "invalid RAID10: nsubstripes != 2 (%u)",
1c6ba0
+				     grub_le_to_cpu16 (chunk->nsubstripes));
1c6ba0
+		}
1c6ba0
+
1c6ba0
 	      break;
1c6ba0
 	    }
1c6ba0
 	  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
1c6ba0
@@ -1153,6 +1196,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
1c6ba0
 
1c6ba0
 	for (j = 0; j < 2; j++)
1c6ba0
 	  {
1c6ba0
+	    grub_size_t est_chunk_alloc = 0;
1c6ba0
+
1c6ba0
 	    grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
1c6ba0
 			  "+0x%" PRIxGRUB_UINT64_T
1c6ba0
 			  " (%d stripes (%d substripes) of %"
1c6ba0
@@ -1165,6 +1210,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
1c6ba0
 	    grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
1c6ba0
 			  addr);
1c6ba0
 
1c6ba0
+	    if (grub_mul (sizeof (struct grub_btrfs_chunk_stripe),
1c6ba0
+			  grub_le_to_cpu16 (chunk->nstripes), &est_chunk_alloc) ||
1c6ba0
+		grub_add (est_chunk_alloc,
1c6ba0
+			  sizeof (struct grub_btrfs_chunk_item), &est_chunk_alloc) ||
1c6ba0
+		est_chunk_alloc > chunk->size)
1c6ba0
+	      {
1c6ba0
+		err = GRUB_ERR_BAD_FS;
1c6ba0
+		break;
1c6ba0
+	      }
1c6ba0
+
1c6ba0
 	    if (is_raid56)
1c6ba0
 	      {
1c6ba0
 		err = btrfs_read_from_chunk (data, chunk, stripen,