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

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