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

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