You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
6.3 KiB
212 lines
6.3 KiB
From 94020540808683e648b56c795f1621cc6362054e Mon Sep 17 00:00:00 2001 |
|
From: Theodore Ts'o <tytso@mit.edu> |
|
Date: Sun, 1 Sep 2019 00:59:16 -0400 |
|
Subject: [PATCH 3/7] libsupport: add checks to prevent buffer overrun bugs in |
|
quota code |
|
|
|
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. |
|
|
|
RHBZ: 1768710 |
|
Addresses: CVE-2019-5094 |
|
Addresses: TALOS-2019-0887 |
|
Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
|
--- |
|
lib/quota/mkquota.c | 1 + |
|
lib/quota/quotaio_tree.c | 70 +++++++++++++++++++++++++++------------- |
|
lib/quota/quotaio_v2.c | 28 ++++++++++++++++ |
|
3 files changed, 76 insertions(+), 23 deletions(-) |
|
|
|
diff --git a/lib/quota/mkquota.c b/lib/quota/mkquota.c |
|
index 3aa81003..f1fc64a0 100644 |
|
--- a/lib/quota/mkquota.c |
|
+++ b/lib/quota/mkquota.c |
|
@@ -552,6 +552,7 @@ errcode_t quota_compare_and_update(quota_ctx_t qctx, int qtype, |
|
err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data); |
|
if (err) { |
|
log_err("Error scanning dquots"); |
|
+ *usage_inconsistent = 1; |
|
goto out; |
|
} |
|
*usage_inconsistent = scan_data.usage_is_inconsistent; |
|
diff --git a/lib/quota/quotaio_tree.c b/lib/quota/quotaio_tree.c |
|
index c1653a39..2579450e 100644 |
|
--- a/lib/quota/quotaio_tree.c |
|
+++ b/lib/quota/quotaio_tree.c |
|
@@ -534,6 +534,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, |
|
+ type2name(h->qh_type)); |
|
+ return -1; |
|
+ } |
|
+ return 0; |
|
+} |
|
+ |
|
/* |
|
* Scan all dquots in file and call callback on each |
|
*/ |
|
@@ -552,7 +563,7 @@ static int report_block(struct dquot *dquot, uint blk, char *bitmap, |
|
int entries, i; |
|
|
|
if (!buf) |
|
- return 0; |
|
+ return -1; |
|
|
|
set_bit(bitmap, blk); |
|
read_blk(dquot->dq_h, blk, buf); |
|
@@ -574,22 +585,12 @@ static int report_block(struct dquot *dquot, uint blk, char *bitmap, |
|
return entries; |
|
} |
|
|
|
-static void check_reference(struct quota_handle *h, uint 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, |
|
- type2name(h->qh_type)); |
|
-} |
|
|
|
static int report_tree(struct dquot *dquot, uint 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(); |
|
u_int32_t *ref = (u_int32_t *) buf; |
|
|
|
@@ -600,22 +601,40 @@ static int report_tree(struct dquot *dquot, uint blk, int depth, char *bitmap, |
|
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; |
|
} |
|
@@ -634,6 +653,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; |
|
@@ -647,10 +667,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; |
|
} |
|
diff --git a/lib/quota/quotaio_v2.c b/lib/quota/quotaio_v2.c |
|
index e7bf29c3..8957d487 100644 |
|
--- a/lib/quota/quotaio_v2.c |
|
+++ b/lib/quota/quotaio_v2.c |
|
@@ -174,6 +174,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); |
|
@@ -184,6 +186,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; |
|
} |
|
|
|
-- |
|
2.21.1 |
|
|
|
|