| From d8372a209f835b90d0c56a0f7752d47c27a0bd1c Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 25 Mar 2026 16:31:38 +0100 |
| Subject: crypto: deflate - fix spurious -ENOSPC |
| |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| |
| [ Upstream commit 6d89f743e57cb34e233a8217b394c7ee09abf225 ] |
| |
| The code in deflate_decompress_one may erroneously return -ENOSPC even if |
| it didn't run out of output space. The error happens under this |
| condition: |
| |
| - Suppose that there are two input pages, the compressed data fits into |
| the first page and the zlib checksum is placed in the second page. |
| |
| - The code iterates over the first page, decompresses the data and fully |
| fills the destination buffer, zlib_inflate returns Z_OK becuse zlib |
| hasn't seen the checksum yet. |
| |
| - The outer do-while loop is iterated again, acomp_walk_next_src sets the |
| input parameters to the second page containing the checksum. |
| |
| - We go into the inner do-while loop, execute "dcur = |
| acomp_walk_next_dst(&walk);". "dcur" is zero, so we break out of the |
| loop and return -ENOSPC, despite the fact that the decompressed data |
| fit into the destination buffer. |
| |
| In order to fix this bug, this commit changes the logic when to report |
| the -ENOSPC error. We report the error if the destination buffer is empty |
| *and* if zlib_inflate didn't make any progress consuming the input |
| buffer. If zlib_inflate consumes the trailing checksum, we see that it |
| made progress and we will not return -ENOSPC. |
| |
| Fixes: 08cabc7d3c86 ("crypto: deflate - Convert to acomp") |
| Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| crypto/deflate.c | 11 +++++++---- |
| 1 file changed, 7 insertions(+), 4 deletions(-) |
| |
| diff --git a/crypto/deflate.c b/crypto/deflate.c |
| index a3e1fff55661b..8df17e7880c9b 100644 |
| --- a/crypto/deflate.c |
| +++ b/crypto/deflate.c |
| @@ -164,18 +164,21 @@ static int deflate_decompress_one(struct acomp_req *req, |
| |
| do { |
| unsigned int dcur; |
| + unsigned long avail_in; |
| |
| dcur = acomp_walk_next_dst(&walk); |
| - if (!dcur) { |
| - out_of_space = true; |
| - break; |
| - } |
| |
| stream->avail_out = dcur; |
| stream->next_out = walk.dst.virt.addr; |
| + avail_in = stream->avail_in; |
| |
| ret = zlib_inflate(stream, Z_NO_FLUSH); |
| |
| + if (!dcur && avail_in == stream->avail_in) { |
| + out_of_space = true; |
| + break; |
| + } |
| + |
| dcur -= stream->avail_out; |
| acomp_walk_done_dst(&walk, dcur); |
| } while (ret == Z_OK && stream->avail_in); |
| -- |
| 2.53.0 |
| |