]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
erofs: fix unsigned underflow in z_erofs_lz4_handle_overlap()
authorJunrui Luo <moonafterrain@outlook.com>
Thu, 9 Apr 2026 13:59:39 +0000 (21:59 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Thu, 9 Apr 2026 17:45:47 +0000 (01:45 +0800)
Some crafted images can have illegal (!partial_decoding &&
m_llen < m_plen) extents, and the LZ4 inplace decompression path
can be wrongly hit, but it cannot handle (outpages < inpages)
properly: "outpages - inpages" wraps to a large value and
the subsequent rq->out[] access reads past the decompressed_pages
array.

However, such crafted cases can correctly result in a corruption
report in the normal LZ4 non-inplace path.

Let's add an additional check to fix this for backporting.

Reproducible image (base64-encoded gzipped blob):

H4sIAJGR12kCA+3SPUoDQRgG4MkmkkZk8QRbRFIIi9hbpEjrHQI5ghfwCN5BLCzTGtLbBI+g
dilSJo1CnIm7GEXFxhT6PDDwfrs73/ywIQD/1ePD4r7Ou6ETsrq4mu7XcWfj++Pb58nJU/9i
PNtbjhan04/9GtX4qVYc814WDqt6FaX5s+ZwXXeq52lndT6IuVvlblytLMvh4Gzwaf90nsvz
2DF/21+20T/ldgp5s1jXRaN4t/8izsy/OUB6e/Qa79r+JwAAAAAAAL52vQVuGQAAAP6+my1w
ywAAAAAAAADwu14ATsEYtgBQAAA=

$ mount -t erofs -o cache_strategy=disabled foo.erofs /mnt
$ dd if=/mnt/data of=/dev/null bs=4096 count=1

Fixes: 598162d05080 ("erofs: support decompress big pcluster for lz4 backend")
Reported-by: Yuhao Jiang <danisjiang@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Junrui Luo <moonafterrain@outlook.com>
Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
fs/erofs/decompressor.c

index 3c54e95964c9fb9050b568ea3f92fd0956a68542..2b065f8c3f711ed411addb14183253b0272c5b78 100644 (file)
@@ -145,6 +145,7 @@ static void *z_erofs_lz4_handle_overlap(const struct z_erofs_decompress_req *rq,
        oend = rq->pageofs_out + rq->outputsize;
        omargin = PAGE_ALIGN(oend) - oend;
        if (!rq->partial_decoding && may_inplace &&
+           rq->outpages >= rq->inpages &&
            omargin >= LZ4_DECOMPRESS_INPLACE_MARGIN(rq->inputsize)) {
                for (i = 0; i < rq->inpages; ++i)
                        if (rq->out[rq->outpages - rq->inpages + i] !=