[PATCH v2 1/2] erofs-utils: lib: validate ZSTD frame content size in decompression

Utkal Singh singhutkal015 at gmail.com
Tue Mar 17 15:55:36 AEDT 2026


ZSTD_getFrameContentSize() reads the content size from the ZSTD
frame header in the compressed data. This is untrusted on-disk
metadata, independent from the extent map that provides
rq->decodedlength via z_erofs_map_blocks_iter().

A crafted EROFS image can set the extent map to claim a decoded
length larger than the actual ZSTD frame content size. When this
happens, a buffer of the (smaller) frame content size is allocated
and decompressed into, but the subsequent memcpy copies
rq->decodedlength bytes from it -- a potential out-of-bounds read.

Additionally, the ZSTD_getDecompressedSize() legacy fallback
returns 0 for frames without a content size field. This leads to
malloc(0) followed by out-of-bounds access on the returned pointer.

Reject frames where the reported content size is zero or smaller
than the expected decoded length.

Reproducer:
  mkdir testdir
  python3 -c "open('testdir/f','wb').write(b'A'*131072)"
  mkfs.erofs -zzstd test.erofs testdir/
  python3 -c "d=bytearray(open('test.erofs','rb').read());\
    p=d.find(b'\x28\xb5\x2f\xfd');d[p+4]=0x20;d[p+5]=0x01;\
    open('test.erofs','wb').write(d)"
  fsck.erofs --extract=out test.erofs
  # Expected: ZSTD frame content size 1 < decoded length 131072

Signed-off-by: Utkal Singh <singhutkal015 at gmail.com>
---
 lib/decompress.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lib/decompress.c b/lib/decompress.c
index 3e7a173..fb81039 100644
--- a/lib/decompress.c
+++ b/lib/decompress.c
@@ -48,7 +48,14 @@ static int z_erofs_decompress_zstd(struct z_erofs_decompress_req *rq)
 #else
 	total = ZSTD_getDecompressedSize(src + inputmargin,
 					 rq->inputsize - inputmargin);
+	if (!total)
+		return -EFSCORRUPTED;
 #endif
+	if (total < rq->decodedlength) {
+		erofs_err("ZSTD frame content size %llu < decoded length %u",
+			  total, rq->decodedlength);
+		return -EFSCORRUPTED;
+	}
 	if (rq->decodedskip || total != rq->decodedlength) {
 		buff = malloc(total);
 		if (!buff)
-- 
2.43.0



More information about the Linux-erofs mailing list