Commit 9b4841c6 for xz

commit 9b4841c671e63d49d210f9753cb118515a381f0a
Author: Lasse Collin <lasse.collin@tukaani.org>
Date:   Wed Nov 4 18:37:22 2026 +0200

    liblzma: Fix optional LZMA end marker detection

    When uncompressed size was known and EOPM was allowed, the EOPM could
    still be rejected with LZMA_DATA_ERROR. It happened when the decoding
    of the EOPM symbol had been started (eopm_is_valid was set to true),
    but there wasn't enough input to fully decode the EOPM symbol yet.
    On the next lzma_code() call with more input, the local variable
    eopm_is_valid was initialized to false and the EOPM symbol was rejected.

    Fix by storing eopm_is_valid in *coder. Once it has been set to true, it
    can never become false again. (NOTE: One needs to be careful. For example,
    might_finish_without_eopm cannot be put to *coder because a later
    lzma_code() call might provide less output space than an earlier call,
    and so might_finish_without_eopm can flip between true and false.)

    This bug affected (1) decoding of unusual .lzma files and
    (2) raw decoding with LZMA_FILTER_LZMA1EXT when the flag
    LZMA_LZMA1EXT_ALLOW_EOPM is set. Support for (1) was added in
    5.2.6 (2022-08-12) but due to this bug it didn't always work.
    Support for (2) was added in 5.3.5beta (2022-12-01).

    Fixes: 9595a3119b9f ("liblzma: Add optional autodetection of LZMA end marker.")
    Fixes: https://github.com/tukaani-project/xz/issues/260
    Reported-by: GitHub user tiendc

diff --git a/src/liblzma/lzma/lzma_decoder.c b/src/liblzma/lzma/lzma_decoder.c
index 2088a2fa..47383e06 100644
--- a/src/liblzma/lzma/lzma_decoder.c
+++ b/src/liblzma/lzma/lzma_decoder.c
@@ -179,6 +179,15 @@ typedef struct {
 	/// This is ignored if uncompressed_size == LZMA_VLI_UNKNOWN.
 	bool allow_eopm;

+	/// This is true when the next LZMA symbol is allowed to be EOPM.
+	/// That is, if this is false, then EOPM is considered
+	/// an invalid symbol and we will return LZMA_DATA_ERROR.
+	///
+	/// This is always true when uncompressed_size == LZMA_VLI_UNKNOWN.
+	/// If allow_eopm is true, eopm_is_valid might be set to true after
+	/// the expected amount of uncompressed output has been produced.
+	bool eopm_is_valid;
+
 	////////////////////////////////
 	// State of incomplete symbol //
 	////////////////////////////////
@@ -288,15 +297,6 @@ lzma_decode(void *coder_ptr, lzma_dict *restrict dictptr,

 	lzma_ret ret = LZMA_OK;

-	// This is true when the next LZMA symbol is allowed to be EOPM.
-	// That is, if this is false, then EOPM is considered
-	// an invalid symbol and we will return LZMA_DATA_ERROR.
-	//
-	// EOPM is always required (not just allowed) when
-	// the uncompressed size isn't known. When uncompressed size
-	// is known, eopm_is_valid may be set to true later.
-	bool eopm_is_valid = coder->uncompressed_size == LZMA_VLI_UNKNOWN;
-
 	// If uncompressed size is known and there is enough output space
 	// to decode all the data, limit the available buffer space so that
 	// the main loop won't try to decode past the end of the stream.
@@ -684,7 +684,7 @@ slow:

 			// Otherwise continue decoding with the expectation
 			// that the next LZMA symbol is EOPM.
-			eopm_is_valid = true;
+			coder->eopm_is_valid = true;
 		}

 		rc_if_0_safe(coder->is_match[state][pos_state], SEQ_IS_MATCH) {
@@ -856,7 +856,7 @@ slow:
 #ifndef HAVE_SMALL
 eopm:
 #endif
-						if (!eopm_is_valid) {
+						if (!coder->eopm_is_valid) {
 							ret = LZMA_DATA_ERROR;
 							goto out;
 						}
@@ -1028,6 +1028,7 @@ lzma_decoder_uncompressed(void *coder_ptr, lzma_vli uncompressed_size,
 	lzma_lzma1_decoder *coder = coder_ptr;
 	coder->uncompressed_size = uncompressed_size;
 	coder->allow_eopm = allow_eopm;
+	coder->eopm_is_valid = uncompressed_size == LZMA_VLI_UNKNOWN;
 }