Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "S. Biplab Raut" <Biplab.Raut@amd.com>, linux-mm@kvack.org
Cc: "Andrew Morton" <akpm@linux-foundation.org>,
	hsiangkao@linux.alibaba.com, terrelln@fb.com,
	yann.collet.73@gmail.com, guoxuenan@huawei.com
Subject: Re: [PATCH v2] lib/lz4: backport upstream fast decode loop
Date: Sun, 15 Nov 2026 17:15:27 +0000	[thread overview]
Message-ID: <sashiko-outbox-215671@kernel.org> (raw)
In-Reply-To: <20261114171452.1484344-1-Biplab.Raut@amd.com>

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] lib/lz4: 32-bit size_t overflow in LZ4_decompress_generic()
  length addition
--

commit 007dc43642875bedabb77ae7e51314787e661069
Author: S. Biplab Raut <Biplab.Raut@amd.com>

lib/lz4: backport upstream fast decode loop

This commit backports the upstream LZ4 v1.9.2 fast decode loop and shared
variable-length reader into LZ4_decompress_generic(), along with size_t
overflow and match-length bounds checks.

Link: https://github.com/lz4/lz4/commit/28356e02ad6f6dac529302cedf707712c5b628fe
Link: https://github.com/lz4/lz4/pull/645

> diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c
> index 3a2cd9acada4a..1155d5a369f43 100644
> --- a/lib/lz4/lz4_decompress.c
> +++ b/lib/lz4/lz4_decompress.c
> @@ -49,12 +49,176 @@

[ ... ]

> +static FORCE_INLINE size_t
> +read_variable_length(const BYTE **ip, const BYTE *lencheck,
> +		     int loop_check, int initial_check,
> +		     enum variable_length_error *error)
> +{
> +	size_t length = 0;
> +	size_t s;
> +
> +	if (initial_check && unlikely((*ip) >= lencheck)) {
> +		/* overflow detection */
> +		*error = initial_error;
> +		return length;
> +	}
> +
> +	do {
> +		s = **ip;
> +		(*ip)++;
> +		if (unlikely(length > SIZE_MAX - s)) {
> +			*error = overflow_error;
> +			return length;
> +		}
> +		length += s;
> +		if (loop_check && unlikely((*ip) >= lencheck)) {
> +			/* overflow detection */
> +			*error = loop_error;
> +			return length;
> +		}
> +	} while (s == 255);
> +
> +	return length;
> +}

[ ... ]

> @@ -293,44 +655,30 @@ static FORCE_INLINE int LZ4_decompress_generic(
>  		length = token & ML_MASK;
>  
>  _copy_match:
> -		if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) {
> -			/* Error : offset outside buffers */
> -			goto _output_error;
> -		}
> -
> -		/* costs ~1%; silence an msan warning when offset == 0 */
> -		/*
> -		 * note : when partialDecoding, there is no guarantee that
> -		 * at least 4 bytes remain available in output buffer
> -		 */
> -		if (!partialDecoding) {
> -			assert(oend > op);
> -			assert(oend - op >= 4);
> -
> -			LZ4_write32(op, (U32)offset);
> -		}
> -
>  		if (length == ML_MASK) {
> -			unsigned int s;
> -
> -			do {
> -				s = *ip++;
> -
> -				if ((endOnInput) && (ip > iend - LASTLITERALS))
> -					goto _output_error;
> -
> -				length += s;
> -			} while (s == 255);
> +			enum variable_length_error error = ok;
>  
> -			if ((safeDecode)
> -				&& unlikely(
> -					(uptrval)(op) + length < (uptrval)op)) {
> -				/* overflow detection */
> +			length += read_variable_length(&ip,
> +					iend - LASTLITERALS + 1,
> +					(int)endOnInput, 0, &error);
> +			if (error != ok)
>  				goto _output_error;
> -			}
>  		}
>  
>  		length += MINMATCH;
> +		if ((safeDecode) && unlikely((uptrval)(op) +
> +					length < (uptrval)op)) {
> +			/* overflow detection */
> +			goto _output_error;
> +		}

[Severity: High]
Can adding the result of read_variable_length() to length still wrap a
32-bit size_t in LZ4_decompress_generic()?

read_variable_length() starts its local accumulator at 0 and checks
length > SIZE_MAX - s, without accounting for the caller's initial
RUN_MASK (15) on literal lengths or ML_MASK + MINMATCH (19) on match
lengths in both the fast and safe loops.  On 32-bit architectures, a
crafted ~16 MiB extension chain can return a value in [SIZE_MAX - 18,
SIZE_MAX] with error == ok, wrapping length to 0..14 for literals or
0..18 for matches before the op + length and ip + length wrap checks.

When a match length wraps to 0..3 (below MINMATCH), cpy = op + length can
stay at or below oend - MATCH_SAFEGUARD_DISTANCE (12) when op is in
[oend - 15, oend - 12], bypassing the safeguard check and writing up to 4
bytes past oend in the second LZ4_copy8(op, match):

lib/lz4/lz4_decompress.c:LZ4_decompress_generic() {
    ...
		op += 8;

		if (unlikely(cpy > oend - MATCH_SAFEGUARD_DISTANCE)) {
    ...
		} else {
			LZ4_copy8(op, match);
			if (length > 16)
				LZ4_wildCopy(op + 8, match + 8, cpy);
		}
		op = cpy; /* wildcopy correction */
}

Checking for overflow when adding the base length and MINMATCH (or passing
the initial length into read_variable_length()) would prevent the wrap.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114171452.1484344-1-Biplab.Raut@amd.com?part=1


           reply	other threads:[~2026-11-15 17:15 UTC|newest]

Thread overview: expand[flat|nested]  mbox.gz  Atom feed
 [parent not found: <20261114171452.1484344-1-Biplab.Raut@amd.com>]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-215671@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Biplab.Raut@amd.com \
    --cc=akpm@linux-foundation.org \
    --cc=guoxuenan@huawei.com \
    --cc=hsiangkao@linux.alibaba.com \
    --cc=linux-mm@kvack.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=terrelln@fb.com \
    --cc=yann.collet.73@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox