summaryrefslogtreecommitdiffstats
path: root/debian/patches/0091-fs-btrfs-Fix-more-ASAN-and-SEGV-issues-found-with-fu.patch
blob: 4ae22c9bcbfcdd818367c0723e0ff6104edd0282 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
From 22976cf1b9864455173e1bfc617bc63f13fbecf7 Mon Sep 17 00:00:00 2001
From: Darren Kenny <darren.kenny@oracle.com>
Date: Tue, 29 Mar 2022 15:52:46 +0000
Subject: fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing

The fuzzer is generating btrfs file systems that have chunks with
invalid combinations of stripes and substripes for the given RAID
configurations.

After examining the Linux kernel fs/btrfs/tree-checker.c code, it
appears that sub-stripes should only be applied to RAID10, and in that
case there should only ever be 2 of them.

Similarly, RAID single should only have 1 stripe, and RAID1/1C3/1C4
should have 2. 3 or 4 stripes respectively, which is what redundancy
corresponds.

Some of the chunks ended up with a size of 0, which grub_malloc() still
returned memory for and in turn generated ASAN errors later when
accessed.

While it would be possible to specifically limit the number of stripes,
a more correct test was on the combination of the chunk item, and the
number of stripes by the size of the chunk stripe structure in
comparison to the size of the chunk itself.

Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/btrfs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index eb9857d74..b3d71cd9e 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -912,6 +912,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	return grub_error (GRUB_ERR_BAD_FS,
 			   "couldn't find the chunk descriptor");
 
+      if (!chsize)
+	{
+	  grub_dprintf ("btrfs", "zero-size chunk\n");
+	  return grub_error (GRUB_ERR_BAD_FS,
+			     "got an invalid zero-size chunk");
+	}
       chunk = grub_malloc (chsize);
       if (!chunk)
 	return grub_errno;
@@ -970,6 +976,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	      stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
 					     nstripes,
 					     NULL);
+
+	      /* For single, there should be exactly 1 stripe. */
+	      if (grub_le_to_cpu16 (chunk->nstripes) != 1)
+		{
+		  grub_dprintf ("btrfs", "invalid RAID_SINGLE: nstripes != 1 (%u)\n",
+				grub_le_to_cpu16 (chunk->nstripes));
+		  return grub_error (GRUB_ERR_BAD_FS,
+				     "invalid RAID_SINGLE: nstripes != 1 (%u)",
+				      grub_le_to_cpu16 (chunk->nstripes));
+		}
 	      if (stripe_length == 0)
 		stripe_length = 512;
 	      stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
@@ -989,6 +1005,19 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	      stripen = 0;
 	      stripe_offset = off;
 	      csize = grub_le_to_cpu64 (chunk->size) - off;
+
+             /*
+	      * Redundancy, and substripes only apply to RAID10, and there
+	      * should be exactly 2 sub-stripes.
+	      */
+	     if (grub_le_to_cpu16 (chunk->nstripes) != redundancy)
+               {
+                 grub_dprintf ("btrfs", "invalid RAID1: nstripes != %u (%u)\n",
+                               redundancy, grub_le_to_cpu16 (chunk->nstripes));
+                 return grub_error (GRUB_ERR_BAD_FS,
+                                    "invalid RAID1: nstripes != %u (%u)",
+                                    redundancy, grub_le_to_cpu16 (chunk->nstripes));
+               }
 	      break;
 	    }
 	  case GRUB_BTRFS_CHUNK_TYPE_RAID0:
@@ -1025,6 +1054,20 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	      stripe_offset = low + chunk_stripe_length
 		* high;
 	      csize = chunk_stripe_length - low;
+
+	      /*
+	       * Substripes only apply to RAID10, and there
+	       * should be exactly 2 sub-stripes.
+	       */
+	      if (grub_le_to_cpu16 (chunk->nsubstripes) != 2)
+		{
+		  grub_dprintf ("btrfs", "invalid RAID10: nsubstripes != 2 (%u)",
+				grub_le_to_cpu16 (chunk->nsubstripes));
+		  return grub_error (GRUB_ERR_BAD_FS,
+				     "invalid RAID10: nsubstripes != 2 (%u)",
+				     grub_le_to_cpu16 (chunk->nsubstripes));
+		}
+
 	      break;
 	    }
 	  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
@@ -1124,6 +1167,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 
 	for (j = 0; j < 2; j++)
 	  {
+	    grub_size_t est_chunk_alloc = 0;
+
 	    grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
 			  "+0x%" PRIxGRUB_UINT64_T
 			  " (%d stripes (%d substripes) of %"
@@ -1136,6 +1181,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	    grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
 			  addr);
 
+	    if (grub_mul (sizeof (struct grub_btrfs_chunk_stripe),
+			  grub_le_to_cpu16 (chunk->nstripes), &est_chunk_alloc) ||
+		grub_add (est_chunk_alloc,
+			  sizeof (struct grub_btrfs_chunk_item), &est_chunk_alloc) ||
+		est_chunk_alloc > chunk->size)
+	      {
+		err = GRUB_ERR_BAD_FS;
+		break;
+	      }
+
 	    if (is_raid56)
 	      {
 		err = btrfs_read_from_chunk (data, chunk, stripen,