e2fsprogs: Fix CVE-2019-5094 in libsupport
[openwrt/openwrt.git] / package / utils / e2fsprogs / patches / 100-CVE-2019-5094-libsupport.patch
1 From 09fe1fd2a1f9efc3091b4fc61f1876d0785956a8 Mon Sep 17 00:00:00 2001
2 From: Theodore Ts'o <tytso@mit.edu>
3 Date: Sun, 1 Sep 2019 00:59:16 -0400
4 Subject: libsupport: add checks to prevent buffer overrun bugs in quota code
5
6 A maliciously corrupted file systems can trigger buffer overruns in
7 the quota code used by e2fsck. To fix this, add sanity checks to the
8 quota header fields as well as to block number references in the quota
9 tree.
10
11 Addresses: CVE-2019-5094
12 Addresses: TALOS-2019-0887
13 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
14 (cherry picked from commit 8dbe7b475ec5e91ed767239f0e85880f416fc384)
15 ---
16 lib/support/mkquota.c | 1 +
17 lib/support/quotaio_tree.c | 71 ++++++++++++++++++++++++++++++----------------
18 lib/support/quotaio_v2.c | 28 ++++++++++++++++++
19 3 files changed, 76 insertions(+), 24 deletions(-)
20
21 --- a/lib/support/mkquota.c
22 +++ b/lib/support/mkquota.c
23 @@ -671,6 +671,7 @@ errcode_t quota_compare_and_update(quota
24 err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
25 if (err) {
26 log_debug("Error scanning dquots");
27 + *usage_inconsistent = 1;
28 goto out_close_qh;
29 }
30
31 --- a/lib/support/quotaio_tree.c
32 +++ b/lib/support/quotaio_tree.c
33 @@ -540,6 +540,17 @@ struct dquot *qtree_read_dquot(struct qu
34 return dquot;
35 }
36
37 +static int check_reference(struct quota_handle *h, unsigned int blk)
38 +{
39 + if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) {
40 + log_err("Illegal reference (%u >= %u) in %s quota file",
41 + blk, h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
42 + quota_type2name(h->qh_type));
43 + return -1;
44 + }
45 + return 0;
46 +}
47 +
48 /*
49 * Scan all dquots in file and call callback on each
50 */
51 @@ -558,7 +569,7 @@ static int report_block(struct dquot *dq
52 int entries, i;
53
54 if (!buf)
55 - return 0;
56 + return -1;
57
58 set_bit(bitmap, blk);
59 read_blk(dquot->dq_h, blk, buf);
60 @@ -580,23 +591,12 @@ static int report_block(struct dquot *dq
61 return entries;
62 }
63
64 -static void check_reference(struct quota_handle *h, unsigned int blk)
65 -{
66 - if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
67 - log_err("Illegal reference (%u >= %u) in %s quota file. "
68 - "Quota file is probably corrupted.\n"
69 - "Please run e2fsck (8) to fix it.",
70 - blk,
71 - h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
72 - quota_type2name(h->qh_type));
73 -}
74 -
75 static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
76 char *bitmap,
77 int (*process_dquot) (struct dquot *, void *),
78 void *data)
79 {
80 - int entries = 0, i;
81 + int entries = 0, ret, i;
82 dqbuf_t buf = getdqbuf();
83 __le32 *ref = (__le32 *) buf;
84
85 @@ -607,22 +607,40 @@ static int report_tree(struct dquot *dqu
86 if (depth == QT_TREEDEPTH - 1) {
87 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
88 blk = ext2fs_le32_to_cpu(ref[i]);
89 - check_reference(dquot->dq_h, blk);
90 - if (blk && !get_bit(bitmap, blk))
91 - entries += report_block(dquot, blk, bitmap,
92 - process_dquot, data);
93 + if (check_reference(dquot->dq_h, blk)) {
94 + entries = -1;
95 + goto errout;
96 + }
97 + if (blk && !get_bit(bitmap, blk)) {
98 + ret = report_block(dquot, blk, bitmap,
99 + process_dquot, data);
100 + if (ret < 0) {
101 + entries = ret;
102 + goto errout;
103 + }
104 + entries += ret;
105 + }
106 }
107 } else {
108 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
109 blk = ext2fs_le32_to_cpu(ref[i]);
110 if (blk) {
111 - check_reference(dquot->dq_h, blk);
112 - entries += report_tree(dquot, blk, depth + 1,
113 - bitmap, process_dquot,
114 - data);
115 + if (check_reference(dquot->dq_h, blk)) {
116 + entries = -1;
117 + goto errout;
118 + }
119 + ret = report_tree(dquot, blk, depth + 1,
120 + bitmap, process_dquot,
121 + data);
122 + if (ret < 0) {
123 + entries = ret;
124 + goto errout;
125 + }
126 + entries += ret;
127 }
128 }
129 }
130 +errout:
131 freedqbuf(buf);
132 return entries;
133 }
134 @@ -642,6 +660,7 @@ int qtree_scan_dquots(struct quota_handl
135 int (*process_dquot) (struct dquot *, void *),
136 void *data)
137 {
138 + int ret;
139 char *bitmap;
140 struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
141 struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
142 @@ -655,10 +674,14 @@ int qtree_scan_dquots(struct quota_handl
143 ext2fs_free_mem(&dquot);
144 return -1;
145 }
146 - v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap,
147 - process_dquot, data);
148 + ret = report_tree(dquot, QT_TREEOFF, 0, bitmap, process_dquot, data);
149 + if (ret < 0)
150 + goto errout;
151 + v2info->dqi_used_entries = ret;
152 v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
153 + ret = 0;
154 +errout:
155 ext2fs_free_mem(&bitmap);
156 ext2fs_free_mem(&dquot);
157 - return 0;
158 + return ret;
159 }
160 --- a/lib/support/quotaio_v2.c
161 +++ b/lib/support/quotaio_v2.c
162 @@ -175,6 +175,8 @@ static int v2_check_file(struct quota_ha
163 static int v2_init_io(struct quota_handle *h)
164 {
165 struct v2_disk_dqinfo ddqinfo;
166 + struct v2_mem_dqinfo *info;
167 + __u64 filesize;
168
169 h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
170 sizeof(struct v2r1_disk_dqblk);
171 @@ -185,6 +187,32 @@ static int v2_init_io(struct quota_handl
172 sizeof(ddqinfo)) != sizeof(ddqinfo))
173 return -1;
174 v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
175 +
176 + /* Check to make sure quota file info is sane */
177 + info = &h->qh_info.u.v2_mdqi;
178 + if (ext2fs_file_get_lsize(h->qh_qf.e2_file, &filesize))
179 + return -1;
180 + if ((filesize > (1U << 31)) ||
181 + (info->dqi_qtree.dqi_blocks >
182 + (filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS)) {
183 + log_err("Quota inode %u corrupted: file size %llu; "
184 + "dqi_blocks %u", h->qh_qf.ino,
185 + filesize, info->dqi_qtree.dqi_blocks);
186 + return -1;
187 + }
188 + if (info->dqi_qtree.dqi_free_blk >= info->dqi_qtree.dqi_blocks) {
189 + log_err("Quota inode %u corrupted: free_blk %u; dqi_blocks %u",
190 + h->qh_qf.ino, info->dqi_qtree.dqi_free_blk,
191 + info->dqi_qtree.dqi_blocks);
192 + return -1;
193 + }
194 + if (info->dqi_qtree.dqi_free_entry >= info->dqi_qtree.dqi_blocks) {
195 + log_err("Quota inode %u corrupted: free_entry %u; "
196 + "dqi_blocks %u", h->qh_qf.ino,
197 + info->dqi_qtree.dqi_free_entry,
198 + info->dqi_qtree.dqi_blocks);
199 + return -1;
200 + }
201 return 0;
202 }
203