]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
erofs: avoid infinite loop due to incomplete zstd-compressed data
authorGao Xiang <hsiangkao@linux.alibaba.com>
Fri, 31 Oct 2025 05:47:39 +0000 (13:47 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Nov 2025 09:35:49 +0000 (10:35 +0100)
[ Upstream commit f2a12cc3b97f062186568a7b94ddb7aa2ef68140 ]

Currently, the decompression logic incorrectly spins if compressed
data is truncated in crafted (deliberately corrupted) images.

Fixes: 7c35de4df105 ("erofs: Zstandard compression support")
Reported-by: Robert Morris <rtm@csail.mit.edu>
Closes: https://lore.kernel.org/r/50958.1761605413@localhost
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: Chunhai Guo <guochunhai@vivo.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/erofs/decompressor_zstd.c

index 7e177304967e19ecd1ab009c701200165f9d9246..24f4731a7a6d4ce213f691e6d3eb4ce154df166b 100644 (file)
@@ -178,7 +178,6 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
        dctx.bounce = strm->bounce;
 
        do {
-               dctx.avail_out = out_buf.size - out_buf.pos;
                dctx.inbuf_sz = in_buf.size;
                dctx.inbuf_pos = in_buf.pos;
                err = z_erofs_stream_switch_bufs(&dctx, &out_buf.dst,
@@ -194,14 +193,18 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
                in_buf.pos = dctx.inbuf_pos;
 
                zerr = zstd_decompress_stream(stream, &out_buf, &in_buf);
-               if (zstd_is_error(zerr) || (!zerr && rq->outputsize)) {
+               dctx.avail_out = out_buf.size - out_buf.pos;
+               if (zstd_is_error(zerr) ||
+                   ((rq->outputsize + dctx.avail_out) && (!zerr || (zerr > 0 &&
+                               !(rq->inputsize + in_buf.size - in_buf.pos))))) {
                        erofs_err(sb, "failed to decompress in[%u] out[%u]: %s",
                                  rq->inputsize, rq->outputsize,
-                                 zerr ? zstd_get_error_name(zerr) : "unexpected end of stream");
+                                 zstd_is_error(zerr) ? zstd_get_error_name(zerr) :
+                                       "unexpected end of stream");
                        err = -EFSCORRUPTED;
                        break;
                }
-       } while (rq->outputsize || out_buf.pos < out_buf.size);
+       } while (rq->outputsize + dctx.avail_out);
 
        if (dctx.kout)
                kunmap_local(dctx.kout);