[PATCH v2] erofs-utils: fsck: don't allocate/read too large extents
Gao Xiang
hsiangkao at linux.alibaba.com
Fri Jun 2 13:05:19 AEST 2023
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 with 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 at live.com>
Fixes: 412c8f908132 ("erofs-utils: fsck: add --extract=X support to extract to path X")
Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
changes since v1:
- use `unsigned int alloc_rawsize` instead.
fsck/main.c | 62 ++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/fsck/main.c b/fsck/main.c
index ad40537..6f89a1e 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,27 @@ 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);
+ 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 +453,25 @@ 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);
- } else {
- ret = erofs_read_one_data(&map, raw, 0, map.m_plen);
- }
- if (ret)
- goto out;
+ if (ret)
+ goto out;
- 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;
+ if (outfd >= 0 && write(outfd, buffer, map.m_llen) < 0)
+ goto fail_eio;
+ } else {
+ u64 count, p = 0;
+
+ do {
+ 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 +486,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.24.4
More information about the Linux-erofs
mailing list