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
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
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)
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(-)
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);
26 log_debug("Error scanning dquots");
27 + *usage_inconsistent = 1;
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
37 +static int check_reference(struct quota_handle *h, unsigned int blk)
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));
49 * Scan all dquots in file and call callback on each
51 @@ -558,7 +569,7 @@ static int report_block(struct dquot *dq
59 read_blk(dquot->dq_h, blk, buf);
60 @@ -580,23 +591,12 @@ static int report_block(struct dquot *dq
64 -static void check_reference(struct quota_handle *h, unsigned int blk)
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.",
71 - h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
72 - quota_type2name(h->qh_type));
75 static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
77 int (*process_dquot) (struct dquot *, void *),
81 + int entries = 0, ret, i;
82 dqbuf_t buf = getdqbuf();
83 __le32 *ref = (__le32 *) buf;
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)) {
97 + if (blk && !get_bit(bitmap, blk)) {
98 + ret = report_block(dquot, blk, bitmap,
99 + process_dquot, data);
108 for (i = 0; i < QT_BLKSIZE >> 2; i++) {
109 blk = ext2fs_le32_to_cpu(ref[i]);
111 - check_reference(dquot->dq_h, blk);
112 - entries += report_tree(dquot, blk, depth + 1,
113 - bitmap, process_dquot,
115 + if (check_reference(dquot->dq_h, blk)) {
119 + ret = report_tree(dquot, blk, depth + 1,
120 + bitmap, process_dquot,
134 @@ -642,6 +660,7 @@ int qtree_scan_dquots(struct quota_handl
135 int (*process_dquot) (struct dquot *, void *),
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);
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);
151 + v2info->dqi_used_entries = ret;
152 v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
155 ext2fs_free_mem(&bitmap);
156 ext2fs_free_mem(&dquot);
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)
165 struct v2_disk_dqinfo ddqinfo;
166 + struct v2_mem_dqinfo *info;
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))
174 v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
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))
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);
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);
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);