]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
libsupport: add checks to prevent buffer overrun bugs in quota code
authorTheodore Ts'o <tytso@mit.edu>
Sun, 1 Sep 2019 04:59:16 +0000 (00:59 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 23 Sep 2019 17:09:26 +0000 (13:09 -0400)
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>
lib/support/mkquota.c
lib/support/quotaio_tree.c
lib/support/quotaio_v2.c

index 0b9e76659118bc2a6b73b0f065a2cb4bf46ccc3a..ddb5312478196bdd756cf99107e7eef3bce99748 100644 (file)
@@ -671,6 +671,7 @@ errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
        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;
        }
 
index a7c2028c1dc73a69618447384c420825e7203e8a..6cc4fb5b2c1090ffc0f6ed65953e5959e75625cb 100644 (file)
@@ -540,6 +540,17 @@ struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
        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 *dquot, unsigned int blk, char *bitmap,
        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 *dquot, unsigned int blk, char *bitmap,
        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 *dquot, unsigned int blk, int depth,
        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_handle *h,
                      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_handle *h,
                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;
 }
index 38be2a34ffae2c92b3a593e9f421888383be1df3..739066761022b5bdd4cd36558a38c1177b46ba3b 100644 (file)
@@ -175,6 +175,8 @@ static int v2_check_file(struct quota_handle *h, int type, int fmt)
 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_handle *h)
                         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;
 }