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

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