Blame SOURCES/e2fsprogs-1.42.9-libsupport-add-checks-to-prevent-buffer-overrun-bugs.patch

3b974a
From 94020540808683e648b56c795f1621cc6362054e Mon Sep 17 00:00:00 2001
3b974a
From: Theodore Ts'o <tytso@mit.edu>
3b974a
Date: Sun, 1 Sep 2019 00:59:16 -0400
3b974a
Subject: [PATCH 3/7] libsupport: add checks to prevent buffer overrun bugs in
3b974a
 quota code
3b974a
3b974a
A maliciously corrupted file systems can trigger buffer overruns in
3b974a
the quota code used by e2fsck.  To fix this, add sanity checks to the
3b974a
quota header fields as well as to block number references in the quota
3b974a
tree.
3b974a
3b974a
RHBZ: 1768710
3b974a
Addresses: CVE-2019-5094
3b974a
Addresses: TALOS-2019-0887
3b974a
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
3b974a
---
3b974a
 lib/quota/mkquota.c      |  1 +
3b974a
 lib/quota/quotaio_tree.c | 70 +++++++++++++++++++++++++++-------------
3b974a
 lib/quota/quotaio_v2.c   | 28 ++++++++++++++++
3b974a
 3 files changed, 76 insertions(+), 23 deletions(-)
3b974a
3b974a
diff --git a/lib/quota/mkquota.c b/lib/quota/mkquota.c
3b974a
index 3aa81003..f1fc64a0 100644
3b974a
--- a/lib/quota/mkquota.c
3b974a
+++ b/lib/quota/mkquota.c
3b974a
@@ -552,6 +552,7 @@ errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype,
3b974a
 	err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
3b974a
 	if (err) {
3b974a
 		log_err("Error scanning dquots");
3b974a
+		*usage_inconsistent = 1;
3b974a
 		goto out;
3b974a
 	}
3b974a
 	*usage_inconsistent = scan_data.usage_is_inconsistent;
3b974a
diff --git a/lib/quota/quotaio_tree.c b/lib/quota/quotaio_tree.c
3b974a
index c1653a39..2579450e 100644
3b974a
--- a/lib/quota/quotaio_tree.c
3b974a
+++ b/lib/quota/quotaio_tree.c
3b974a
@@ -534,6 +534,17 @@ struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
3b974a
 	return dquot;
3b974a
 }
3b974a
 
3b974a
+static int check_reference(struct quota_handle *h, unsigned int blk)
3b974a
+{
3b974a
+	if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) {
3b974a
+		log_err("Illegal reference (%u >= %u) in %s quota file",
3b974a
+			blk, h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
3b974a
+			type2name(h->qh_type));
3b974a
+		return -1;
3b974a
+	}
3b974a
+	return 0;
3b974a
+}
3b974a
+
3b974a
 /*
3b974a
  * Scan all dquots in file and call callback on each
3b974a
  */
3b974a
@@ -552,7 +563,7 @@ static int report_block(struct dquot *dquot, uint blk, char *bitmap,
3b974a
 	int entries, i;
3b974a
 
3b974a
 	if (!buf)
3b974a
-		return 0;
3b974a
+		return -1;
3b974a
 
3b974a
 	set_bit(bitmap, blk);
3b974a
 	read_blk(dquot->dq_h, blk, buf);
3b974a
@@ -574,22 +585,12 @@ static int report_block(struct dquot *dquot, uint blk, char *bitmap,
3b974a
 	return entries;
3b974a
 }
3b974a
 
3b974a
-static void check_reference(struct quota_handle *h, uint blk)
3b974a
-{
3b974a
-	if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks)
3b974a
-		log_err("Illegal reference (%u >= %u) in %s quota file. "
3b974a
-			"Quota file is probably corrupted.\n"
3b974a
-			"Please run e2fsck (8) to fix it.",
3b974a
-			blk,
3b974a
-			h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
3b974a
-			type2name(h->qh_type));
3b974a
-}
3b974a
 
3b974a
 static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
3b974a
 		       int (*process_dquot) (struct dquot *, void *),
3b974a
 		       void *data)
3b974a
 {
3b974a
-	int entries = 0, i;
3b974a
+	int entries = 0, ret, i;
3b974a
 	dqbuf_t buf = getdqbuf();
3b974a
 	u_int32_t *ref = (u_int32_t *) buf;
3b974a
 
3b974a
@@ -600,22 +601,40 @@ static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap,
3b974a
 	if (depth == QT_TREEDEPTH - 1) {
3b974a
 		for (i = 0; i < QT_BLKSIZE >> 2; i++) {
3b974a
 			blk = ext2fs_le32_to_cpu(ref[i]);
3b974a
-			check_reference(dquot->dq_h, blk);
3b974a
-			if (blk && !get_bit(bitmap, blk))
3b974a
-				entries += report_block(dquot, blk, bitmap,
3b974a
-							process_dquot, data);
3b974a
+			if (check_reference(dquot->dq_h, blk)) {
3b974a
+				entries = -1;
3b974a
+				goto errout;
3b974a
+			}
3b974a
+			if (blk && !get_bit(bitmap, blk)) {
3b974a
+				ret = report_block(dquot, blk, bitmap,
3b974a
+						   process_dquot, data);
3b974a
+				if (ret < 0) {
3b974a
+					entries = ret;
3b974a
+					goto errout;
3b974a
+				}
3b974a
+				entries += ret;
3b974a
+			}
3b974a
 		}
3b974a
 	} else {
3b974a
 		for (i = 0; i < QT_BLKSIZE >> 2; i++) {
3b974a
 			blk = ext2fs_le32_to_cpu(ref[i]);
3b974a
 			if (blk) {
3b974a
-				check_reference(dquot->dq_h, blk);
3b974a
-				entries += report_tree(dquot, blk, depth + 1,
3b974a
-						       bitmap, process_dquot,
3b974a
-						       data);
3b974a
+				if (check_reference(dquot->dq_h, blk)) {
3b974a
+					entries = -1;
3b974a
+					goto errout;
3b974a
+				}
3b974a
+				ret = report_tree(dquot, blk, depth + 1,
3b974a
+						  bitmap, process_dquot,
3b974a
+						  data);
3b974a
+				if (ret < 0) {
3b974a
+					entries = ret;
3b974a
+					goto errout;
3b974a
+				}
3b974a
+				entries += ret;
3b974a
 			}
3b974a
 		}
3b974a
 	}
3b974a
+errout:
3b974a
 	freedqbuf(buf);
3b974a
 	return entries;
3b974a
 }
3b974a
@@ -634,6 +653,7 @@ int qtree_scan_dquots(struct quota_handle *h,
3b974a
 		      int (*process_dquot) (struct dquot *, void *),
3b974a
 		      void *data)
3b974a
 {
3b974a
+	int ret;
3b974a
 	char *bitmap;
3b974a
 	struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
3b974a
 	struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
3b974a
@@ -647,10 +667,14 @@ int qtree_scan_dquots(struct quota_handle *h,
3b974a
 		ext2fs_free_mem(&dquot);
3b974a
 		return -1;
3b974a
 	}
3b974a
-	v2info->dqi_used_entries = report_tree(dquot, QT_TREEOFF, 0, bitmap,
3b974a
-					       process_dquot, data);
3b974a
+	ret = report_tree(dquot, QT_TREEOFF, 0, bitmap, process_dquot, data);
3b974a
+	if (ret < 0)
3b974a
+		goto errout;
3b974a
+	v2info->dqi_used_entries = ret;
3b974a
 	v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
3b974a
+	ret = 0;
3b974a
+errout:
3b974a
 	ext2fs_free_mem(&bitmap);
3b974a
 	ext2fs_free_mem(&dquot);
3b974a
-	return 0;
3b974a
+	return ret;
3b974a
 }
3b974a
diff --git a/lib/quota/quotaio_v2.c b/lib/quota/quotaio_v2.c
3b974a
index e7bf29c3..8957d487 100644
3b974a
--- a/lib/quota/quotaio_v2.c
3b974a
+++ b/lib/quota/quotaio_v2.c
3b974a
@@ -174,6 +174,8 @@ static int v2_check_file(struct quota_handle *h, int type, int fmt)
3b974a
 static int v2_init_io(struct quota_handle *h)
3b974a
 {
3b974a
 	struct v2_disk_dqinfo ddqinfo;
3b974a
+	struct v2_mem_dqinfo *info;
3b974a
+	__u64 filesize;
3b974a
 
3b974a
 	h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
3b974a
 		sizeof(struct v2r1_disk_dqblk);
3b974a
@@ -184,6 +186,32 @@ static int v2_init_io(struct quota_handle *h)
3b974a
 			 sizeof(ddqinfo)) != sizeof(ddqinfo))
3b974a
 		return -1;
3b974a
 	v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
3b974a
+
3b974a
+	/* Check to make sure quota file info is sane */
3b974a
+	info = &h->qh_info.u.v2_mdqi;
3b974a
+	if (ext2fs_file_get_lsize(h->qh_qf.e2_file, &filesize))
3b974a
+		return -1;
3b974a
+	if ((filesize > (1U << 31)) ||
3b974a
+	    (info->dqi_qtree.dqi_blocks >
3b974a
+	     (filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS)) {
3b974a
+		log_err("Quota inode %u corrupted: file size %llu; "
3b974a
+			"dqi_blocks %u", h->qh_qf.ino,
3b974a
+			filesize, info->dqi_qtree.dqi_blocks);
3b974a
+		return -1;
3b974a
+	}
3b974a
+	if (info->dqi_qtree.dqi_free_blk >= info->dqi_qtree.dqi_blocks) {
3b974a
+		log_err("Quota inode %u corrupted: free_blk %u; dqi_blocks %u",
3b974a
+			h->qh_qf.ino, info->dqi_qtree.dqi_free_blk,
3b974a
+			info->dqi_qtree.dqi_blocks);
3b974a
+		return -1;
3b974a
+	}
3b974a
+	if (info->dqi_qtree.dqi_free_entry >= info->dqi_qtree.dqi_blocks) {
3b974a
+		log_err("Quota inode %u corrupted: free_entry %u; "
3b974a
+			"dqi_blocks %u", h->qh_qf.ino,
3b974a
+			info->dqi_qtree.dqi_free_entry,
3b974a
+			info->dqi_qtree.dqi_blocks);
3b974a
+		return -1;
3b974a
+	}
3b974a
 	return 0;
3b974a
 }
3b974a
 
3b974a
-- 
3b974a
2.21.1
3b974a