aboutsummaryrefslogtreecommitdiffstats
path: root/package/utils/e2fsprogs/patches/100-CVE-2019-5094-libsupport.patch
blob: c69f4650524ea403df3cb7326371c39c18277ea7 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
From 09fe1fd2a1f9efc3091b4fc61f1876d0785956a8 Mon Sep 17 00:00:00 2001
From: Theodore Ts'o <tytso@mit.edu>
Date: Sun, 1 Sep 2019 00:59:16 -0400
Subject: libsupport: add checks to prevent buffer overrun bugs in quota code

A maliciously corrupted file systems can trigger buffer overruns in
the quota code used by e2fsck.  To fix this, add sanity checks to the
quota header fields as well as to block number references in the quota
tree.

Addresses: CVE-2019-5094
Addresses: TALOS-2019-0887
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
(cherry picked from commit 8dbe7b475ec5e91ed767239f0e85880f416fc384)
---
 lib/support/mkquota.c      |  1 +
 lib/support/quotaio_tree.c | 71 ++++++++++++++++++++++++++++++----------------
 lib/support/quotaio_v2.c   | 28 ++++++++++++++++++
 3 files changed, 76 insertions(+), 24 deletions(-)

--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -671,6 +671,7 @@ errcode_t quota_compare_and_update(quota
 	err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
 	if (err) {
 		log_debug("Error scanning dquots");
+		*usage_inconsistent = 1;
 		goto out_close_qh;
 	}
 
--- a/lib/support/quotaio_tree.c
+++ b/lib/support/quotaio_tree.c
@@ -540,6 +540,17 @@ struct dquot *qtree_read_dquot(struct qu
 	return dquot;
 }
 
+static int check_reference(struct quota_handle *h, unsigned int blk)
+{
+	if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) {
+		log_err("Illegal reference (%u >= %u) in %s quota file",
+			blk, h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
+			quota_type2name(h->qh_type));
+		return -1;
+	}
+	return 0;
+}
+
 /*
  * Scan all dquots in file and call callback on each
  */
@@ -558,7 +569,7 @@ static int report_block(struct dquot *dq
 	int entries, i;
 
 	if (!buf)
-		return 0;
+		return -1;
 
 	set_bit(bitmap, blk);
 	read_blk(dquot->dq_h, blk, buf);
@@ -580,23 +591,12 @@ static int report_block(struct dquot *dq
 	return entries;
 }
 
-static void check_reference(struct quota_handle *h, unsigned int blk)
-{
-	if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
-		log_err("Illegal reference (%u >= %u) in %s quota file. "
-			"Quota file is probably corrupted.\n"
-			"Please run e2fsck (8) to fix it.",
-			blk,
-			h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
-			quota_type2name(h->qh_type));
-}
-
 static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
 		       char *bitmap,
 		       int (*process_dquot) (struct dquot *, void *),
 		       void *data)
 {
-	int entries = 0, i;
+	int entries = 0, ret, i;
 	dqbuf_t buf = getdqbuf();
 	__le32 *ref = (__le32 *) buf;
 
@@ -607,22 +607,40 @@ static int report_tree(struct dquot *dqu
 	if (depth == QT_TREEDEPTH - 1) {
 		for (i = 0; i < QT_BLKSIZE >> 2; i++) {
 			blk = ext2fs_le32_to_cpu(ref[i]);
-			check_reference(dquot->dq_h, blk);
-			if (blk && !get_bit(bitmap, blk))
-				entries += report_block(dquot, blk, bitmap,
-							process_dquot, data);
+			if (check_reference(dquot->dq_h, blk)) {
+				entries = -1;
+				goto errout;
+			}
+			if (blk && !get_bit(bitmap, blk)) {
+				ret = report_block(dquot, blk, bitmap,
+						   process_dquot, data);
+				if (ret < 0) {
+					entries = ret;
+					goto errout;
+				}
+				entries += ret;
+			}
 		}
 	} else {
 		for (i = 0; i < QT_BLKSIZE >> 2; i++) {
 			blk = ext2fs_le32_to_cpu(ref[i]);
 			if (blk) {
-				check_reference(dquot->dq_h, blk);
-				entries += report_tree(dquot, blk, depth + 1,
-						       bitmap, process_dquot,
-						       data);
+				if (check_reference(dquot->dq_h, blk)) {
+					entries = -1;
+					goto errout;
+				}
+				ret = report_tree(dquot, blk, depth + 1,
+						  bitmap, process_dquot,
+						  data);
+				if (ret < 0) {
+					entries = ret;
+					goto errout;
+				}
+				entries += ret;
 			}
 		}
 	}
+errout:
 	freedqbuf(buf);
 	return entries;
 }
@@ -642,6 +660,7 @@ int qtree_scan_dquots(struct quota_handl
 		      int (*process_dquot) (struct dquot *, void *),
 		      void *data)
 {
+	int ret;
 	char *bitmap;
 	struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
 	struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
@@ -655,10 +674,14 @@ int qtree_scan_dquots(struct quota_handl
 		ext2fs_free_mem(&dquot);
 		return -1;
 	}
-	v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap,
-					       process_dquot, data);
+	ret = report_tree(dquot, QT_TREEOFF, 0, bitmap, process_dquot, data);
+	if (ret < 0)
+		goto errout;
+	v2info->dqi_used_entries = ret;
 	v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
+	ret = 0;
+errout:
 	ext2fs_free_mem(&bitmap);
 	ext2fs_free_mem(&dquot);
-	return 0;
+	return ret;
 }
--- a/lib/support/quotaio_v2.c
+++ b/lib/support/quotaio_v2.c
@@ -175,6 +175,8 @@ static int v2_check_file(struct quota_ha
 static int v2_init_io(struct quota_handle *h)
 {
 	struct v2_disk_dqinfo ddqinfo;
+	struct v2_mem_dqinfo *info;
+	__u64 filesize;
 
 	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
 		sizeof(struct v2r1_disk_dqblk);
@@ -185,6 +187,32 @@ static int v2_init_io(struct quota_handl
 			 sizeof(ddqinfo)) != sizeof(ddqinfo))
 		return -1;
 	v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
+
+	/* Check to make sure quota file info is sane */
+	info = &h->qh_info.u.v2_mdqi;
+	if (ext2fs_file_get_lsize(h->qh_qf.e2_file, &filesize))
+		return -1;
+	if ((filesize > (1U << 31)) ||
+	    (info->dqi_qtree.dqi_blocks >
+	     (filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS)) {
+		log_err("Quota inode %u corrupted: file size %llu; "
+			"dqi_blocks %u", h->qh_qf.ino,
+			filesize, info->dqi_qtree.dqi_blocks);
+		return -1;
+	}
+	if (info->dqi_qtree.dqi_free_blk >= info->dqi_qtree.dqi_blocks) {
+		log_err("Quota inode %u corrupted: free_blk %u; dqi_blocks %u",
+			h->qh_qf.ino, info->dqi_qtree.dqi_free_blk,
+			info->dqi_qtree.dqi_blocks);
+		return -1;
+	}
+	if (info->dqi_qtree.dqi_free_entry >= info->dqi_qtree.dqi_blocks) {
+		log_err("Quota inode %u corrupted: free_entry %u; "
+			"dqi_blocks %u", h->qh_qf.ino,
+			info->dqi_qtree.dqi_free_entry,
+			info->dqi_qtree.dqi_blocks);
+		return -1;
+	}
 	return 0;
 }