nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone

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

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