e2fsprogs: Fix CVE-2019-5094 in libsupport
[librecmc/librecmc.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