|
|
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 |
|