| From c769805c79d5acede65d96e5786aa5ebb46c01e0 Mon Sep 17 00:00:00 2001 |
| From: Gao Xiang <hsiangkao@linux.alibaba.com> |
| Date: Fri, 2 Jun 2023 11:05:19 +0800 |
| Subject: [PATCH 1/2] erofs-utils: fsck: don't allocate/read too large extents |
| |
| Since some crafted EROFS filesystem images could have insane large |
| extents, which causes unexpected bahaviors when extracting data. |
| |
| Fix it by extracting large extents with a buffer of a reasonable |
| maximum size limit and reading multiple times instead. |
| |
| Note that only `--extract` option is impacted. |
| |
| CVE: CVE-2023-33552 |
| Closes: https://nvd.nist.gov/vuln/detail/CVE-2023-33552 |
| Reported-by: Chaoming Yang <lometsj@live.com> |
| Fixes: 412c8f908132 ("erofs-utils: fsck: add --extract=X support to extract to path X") |
| Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com> |
| Link: https://lore.kernel.org/r/20230602030519.117071-1-hsiangkao@linux.alibaba.com |
| |
| Upstream-Status: Backport |
| Signed-off-by: Ross Burton <ross.burton@arm.com> |
| --- |
| fsck/main.c | 63 +++++++++++++++++++++++++++++++++++++++++------------ |
| 1 file changed, 49 insertions(+), 14 deletions(-) |
| |
| diff --git a/fsck/main.c b/fsck/main.c |
| index 6b42252..6689ad8 100644 |
| --- a/fsck/main.c |
| +++ b/fsck/main.c |
| @@ -392,6 +392,8 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd) |
| } |
| |
| while (pos < inode->i_size) { |
| + unsigned int alloc_rawsize; |
| + |
| map.m_la = pos; |
| if (compressed) |
| ret = z_erofs_map_blocks_iter(inode, &map, |
| @@ -420,10 +422,28 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd) |
| if (!(map.m_flags & EROFS_MAP_MAPPED) || !fsckcfg.check_decomp) |
| continue; |
| |
| - if (map.m_plen > raw_size) { |
| - raw_size = map.m_plen; |
| - raw = realloc(raw, raw_size); |
| - BUG_ON(!raw); |
| + if (map.m_plen > Z_EROFS_PCLUSTER_MAX_SIZE) { |
| + if (compressed) { |
| + erofs_err("invalid pcluster size %" PRIu64 " @ offset %" PRIu64 " of nid %" PRIu64, |
| + map.m_plen, map.m_la, |
| + inode->nid | 0ULL); |
| + ret = -EFSCORRUPTED; |
| + goto out; |
| + } |
| + alloc_rawsize = Z_EROFS_PCLUSTER_MAX_SIZE; |
| + } else { |
| + alloc_rawsize = map.m_plen; |
| + } |
| + |
| + if (alloc_rawsize > raw_size) { |
| + char *newraw = realloc(raw, alloc_rawsize); |
| + |
| + if (!newraw) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| + raw = newraw; |
| + raw_size = alloc_rawsize; |
| } |
| |
| if (compressed) { |
| @@ -434,18 +454,27 @@ static int erofs_verify_inode_data(struct erofs_inode *inode, int outfd) |
| } |
| ret = z_erofs_read_one_data(inode, &map, raw, buffer, |
| 0, map.m_llen, false); |
| + if (ret) |
| + goto out; |
| + |
| + if (outfd >= 0 && write(outfd, buffer, map.m_llen) < 0) |
| + goto fail_eio; |
| } else { |
| - ret = erofs_read_one_data(&map, raw, 0, map.m_plen); |
| - } |
| - if (ret) |
| - goto out; |
| + u64 p = 0; |
| |
| - if (outfd >= 0 && write(outfd, compressed ? buffer : raw, |
| - map.m_llen) < 0) { |
| - erofs_err("I/O error occurred when verifying data chunk @ nid %llu", |
| - inode->nid | 0ULL); |
| - ret = -EIO; |
| - goto out; |
| + do { |
| + u64 count = min_t(u64, alloc_rawsize, |
| + map.m_llen); |
| + |
| + ret = erofs_read_one_data(&map, raw, p, count); |
| + if (ret) |
| + goto out; |
| + |
| + if (outfd >= 0 && write(outfd, raw, count) < 0) |
| + goto fail_eio; |
| + map.m_llen -= count; |
| + p += count; |
| + } while (map.m_llen); |
| } |
| } |
| |
| @@ -460,6 +489,12 @@ out: |
| if (buffer) |
| free(buffer); |
| return ret < 0 ? ret : 0; |
| + |
| +fail_eio: |
| + erofs_err("I/O error occurred when verifying data chunk @ nid %llu", |
| + inode->nid | 0ULL); |
| + ret = -EIO; |
| + goto out; |
| } |
| |
| static inline int erofs_extract_dir(struct erofs_inode *inode) |
| -- |
| 2.34.1 |
| |